Programming PIC with C - ace.cs.ohio.edu
Transcript of Programming PIC with C - ace.cs.ohio.edu
2/27/2019
1
Programming PIC with C
Spring 2019EE3954: Microprocessors and Microcontrollers
Avinash Karanth
Professor, Electrical Engineering and Computer Science
Ohio University
E-mail: [email protected]: Harsha Chenji, Jim Goble, Maarten Uijt de Haag
1
Course Administration
• Lab 3 this week!
• Homework 3 to be posted by end of week – due next Friday March 8th
• No lab next week!
2
1
2
2/27/2019
2
Reference Sections
• MPLAB® XC8 C Compiler User’s Guide
3
C vs Assembler
4
Assembly Code
MachineCode
EEPROM
CCode
MachineCode
EEPROM
Assembly Code
High-level language
Low-level language/Native language
Machine code words
Hardware
3
4
2/27/2019
3
Embedded C
5
• Myth Busting –
• C is not as portable between architectures or compilers as everyone claims• ANSI language features ARE portable• Processor specific libraries are NOT portable• Processor specific code (peripherals, I/O,
interrupts, special features) are NOT portable
• C is NOT as efficient as assembly• A good assembly programmer can usually do
better than the compiler, no matter the optimization employed - C WILL use more memory
Source Code to Silicon
6
5
6
2/27/2019
4
A Simple C Program
7
• The line numbers are just for reference.
• Lines 1 and 3 are preprocessor directives.
• Just like any other C program, we must have only one main function.
• This is a very simple program, but it demonstrates how much easier it is to program the PIC in C than Assembly. There is no defined way to do floating point or even multiplication in assembly. You have to write those routines yourself. In C, they are just part of the normal include files.
A Simple C Program
8
• Variables have to be defined before we use them. Harder than MatLab, much easier than assembly.
• Note that float is a data type. PIC C supports all of the normal data types.
• Unlike most PCs we will program, we should keep track of the bytes we are using with our data. Floats are 4 bytes, chars 1 byte, ints 2 bytes, etc. Why you say? Because we only have 8 KB of program memory and 1 KB of data memory.
7
8
2/27/2019
5
A Simple C Program
9
• The identifiers for our variables follow the normal C convention. They must be composed of combinations of the letters of the alphabet, the numbers 0-9 and the underscore. They are case sensitive.
• The identifiers cannot use any of the normal ANSI C keywords or any of the 12 additional keywords used by the PIC compiler. These will give you compiler errors at best.
Literal Constants
10
• A literal constant or simply a literal is a value, such as a number, character, or string that may be assigned to a variable or symbolic constant, used as an operand in an arithmetic or logical operation, or as a parameter to a function.
• Literals are "hard coded" values such as the number 5, the character 'A', or the string, "Hello, world!".
• Numeric literals may be represented in a variety of formats (decimal, hexadecimal, binary, floating point, etc.)
• A literal always represents the same value (5 always represents the quantity of five)
9
10
2/27/2019
6
Literal vs. Constants
11
• Although most of the C programming world uses the terms constant and literal interchangeably, those with assembly language background will understand them to describe two distinctly different, though related concepts.
• The numbers 32767 and -32768 are literals. They are actual values and the symbols we use to represent them cannot ever mean anything other than the values they represent.
• The words MAXINT and MININT are constants. They are symbols used to represent the values we assigned to them (32767 and -32768 respectively), but the symbols could mean anything we want.
Operators
12
• We won’t be covering many of the operators, since you already have covered them in previous classes. The memory addressing operators take the place of the FSR and INDF assembly commands and do a few things extra. The cost is always memory space.
11
12
2/27/2019
7
Statements
13
• Statements behave exactly the same way they do in any other C compiler. Unlike C on our lab workstations, you have to be aware of memory limitations. Especially since we are using Microchip’s free compiler. The free version is not vey efficient at unpacking. A while statement may use far more memory than you expect, especially compared to the bit test statements used in the assembler.
Functions
14
• Again, functions behave exactly the same way they do in any other C compiler. Again, you have to be aware of memory limitations. A simple looking expression like the one below could use far more memory than you expect.
13
14
2/27/2019
8
Labs
• Lab 1 introduced you to the MPLabdevelopment environment.
• Other than the warnings concerning memory, developing in C will make your life much easier.
• Use of the following configuration slides in setting up your programs will make life easier still.
15
MPLab XC8 C Compiler
• The MPLAB XC8 C Compiler is a free-standing, optimizing ISO C90 (popularly known as ANSI C) compiler. It supports all 8-bit PIC® microcontrollers: PIC10, PIC12, PIC16 and PIC18 series devices.
• It comes in free, standard, and Pro versions. The free version is installed in the labs. You may download this from MicroChip for the PC, Linux, or Mac.
16
15
16
2/27/2019
9
MPLab XC8 C Compiler
• The C language implemented on MPLAB XC8 C Compiler can diverge from the ANSI C Standard in several areas.
• One divergence is due to limited device memory and no hardware implementation of a data stack. For this reason, recursion is not supported.
• Another divergence from the Standard is that you cannot reliably use the C sizeof operator with pointer types; however, this operator may be used with pointer variable identifiers.
17
Main Template
18
/* * File: newmain.c* Author: Maarten Uijt de Haag** Created on January 3, 2017*/
// Insert CONFIG bits
#define _XTAL_FREQ 4000000 // oscillator frequency definition
#include <xc.h>#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <stdbool.h>
// Global variables
// Functions
int main(int argc, char** argv) {
return (EXIT_SUCCESS);}
Can be replaced by void main(void) as well
17
18
2/27/2019
10
Configuration Bits
• Assembly:
• C (in a separate header files: config.h):
; Include the PIC16F18875 header file that defines all special purpose registers and core registers#include "p16F18875.inc”
; Set PIC16F18875 Configuration Bit Settings __CONFIG _CONFIG1, _FEXTOSC_ECH & _RSTOSC_EXT1X & _CLKOUTEN_OFF & _CSWEN_ON & _FCMEN_ON __CONFIG _CONFIG2, _MCLRE_ON & _PWRTE_OFF & _LPBOREN_OFF & _BOREN_ON & _BORV_LO & _ZCD_OFF & _PPS1WAY_ON & _STVREN_ON __CONFIG _CONFIG3, _WDTCPS_WDTCPS_31 & _WDTE_OFF & _WDTCWS_WDTCWS_7 & _WDTCCS_SC __CONFIG _CONFIG4, _WRT_OFF & _SCANE_available & _LVP_ON __CONFIG _CONFIG5, _CP_OFF & _CPD_OFF
// CONFIG1#pragma config FEXTOSC = LP // External Oscillator mode selection bits (EC above 8MHz; PFM set to high power)#pragma config RSTOSC = HFINT1 // Power-up default value for COSC bits (HFINTOSC (1MHz))#pragma config CLKOUTEN = OFF // Clock Out Enable bit (CLKOUT function is disabled; i/o or oscillator function on OSC2)#pragma config CSWEN = ON // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable bit (FSCM timer enabled)// CONFIG2#pragma config MCLRE = ON // Master Clear Enable bit (MCLR pin is Master Clear function)#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)#pragma config LPBOREN = OFF // Low-Power BOR enable bit (ULPBOR disabled)#pragma config BOREN = ON // Brown-out reset enable bits (Brown-out Reset Enabled, SBOREN bit is ignored)#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (VBOR) set to 1.9V on LF, and 2.45V on F Devices)#pragma config ZCD = OFF // Zero-cross detect disable (Zero-cross detect circuit is disabled at POR.)#pragma config PPS1WAY = ON // Peripheral Pin Select one-way control (The PPSLOCK bit can be cleared and set only once in software)#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a reset)// CONFIG3#pragma config WDTCPS = WDTCPS_31// WDT Period Select bits (Divider ratio 1:65536; software control of WDTPS)#pragma config WDTE = OFF // WDT operating mode (WDT enabled regardless of sleep; SWDTEN ignored)#pragma config WDTCWS = WDTCWS_7// WDT Window Select bits (window always open (100%); software control; keyed access not required)#pragma config WDTCCS = SC // WDT input clock selector (Software Control)// CONFIG4#pragma config WRT = OFF // UserNVM self-write protection bits (Write protection off)#pragma config SCANE = available// Scanner Enable bit (Scanner module is available for use)#pragma config LVP = ON // Low Voltage Programming Enable bit (Low Voltage programming enabled. MCLR/Vpp pin function is MCLR.)// CONFIG5#pragma config CP = OFF // UserNVM Program memory code protection bit (Program Memory code protection disabled)#pragma config CPD = OFF // DataNVM code protection bit (Data EEPROM code protection disabled)
How to Access SFRs and Pins
20
void main(void) {
// Configure the I/O portsTRISB = 0b11100001; TRISD = 0b00000000;
}
For example: PORTA, TRISA, RA0, RA1, RCIF, TXSTA, TXIF, GIE, PEIE, SYNC, TXEN, etc. etc.
NO BANK SELECTION REQUIRED
Most Special Function Registers (SFRs) and pins are defined in “xc.h” by their official name as specified in the reference manual and datasheet.
19
20
2/27/2019
11
How to Access Pins
21
void main(void) {
// Configure the I/O portsTRISA = 0x00; TRISB = 0xFF;
RA1 = 1;PORTAbits.RA2 = 1;
if(RB3 == 1) RA3 = 1;else RA3 = 0;
}
Multiple options are available to access a PORT pin:
Example PORTA pin 1: RA1
PORTAbits.RA1
Outputs
OutputsInput
Register Usage
22
The assembly code generated from the C source code will use certain registers in the PIC MCU register set and assumes that nothing other than code it generates
can alter the contents of these registers.
21
22
2/27/2019
12
Variables
23
Non-s
tandard
types
Only integerarithmetic
Variable – Use <stdint.h>
24
C type<stdint.h>
typeBits Sign Range
char uint8_t 8 Unsigned 0 .. 255
signed char int8_t 8 Signed -128 .. 127
unsigned short or unsigned int
uint16_t 16 Unsigned 0 .. 65,535
short or int int16_t 16 Signed -32,768 .. 32,767
unsigned int uint32_t 32 Unsigned 0 .. 4,294,967,295
int int32_t 32 Signed -2,147,483,648 .. 2,147,483,647
unsigned long long
uint64_t 64 Unsigned 0 .. 18,446,744,073,709,551,615
long long int64_t 64 Signed -9,223,372,036,854,775,808 .. 9,223,372,036,854,775,807
23
24
2/27/2019
13
Variables
25
char a;int8_t a;
(_a) 0x75 (for example)
short b;int16_t b;
(_b+1) 0x76 (for example)(_b) 0x75 (for example)(2 bytes)
short long c;int24_t c: (_c+2) 0x77 (for example)
(_c+1) 0x76 (for example)(3 bytes)
(_c) 0x75 (for example)
long d;int32_t d;
(_d+3) 0x78 (for example)(_d+2) 0x77 (for example)(4 bytes)
(_d+1) 0x76 (for example)(_d) 0x75 (for example)
Type Ranges
26
216-1
27-1
Unsigned
Signed
-27
25
26
2/27/2019
14
Use of Functions
27
// Functionschar AddTwoNumbersReturnLowByte(int a, int b);
// Main Program
void main(void) {
TRISA = 0x00;PORTA = AddTwoNumbersReturnLowByte(23456, 12456);
}
char AddTwoNumbersReturnLowByte(int a, int b);{
char ret;int c;
c = a + b;ret = (char)(0x00FF & c);
return(ret);}
16 bits = 2 bytes
Mask most significant byte
Logic AND operation
Use of Functions – Pass by Value
28
char AddTwoNumbersReturnLowByte(int a, int b);{
char ret;int c;
c = a + b;ret = (char)(0x00FF & c);
return(ret);}
Arguments are values:Inputs (two bytes each)Output (byte)
Return the output (byte)
“When passing arguments by value, the only way to return a value back to the caller is via the function’s return value”
Alternative:“Pass by reference” and
“Pass by address”
27
28
2/27/2019
15
‘bit’
29
bit flag; holds the values 0 or 1
bit func(void) {
return(RA0);}
The 1 or 0 value will be returned in the carry flag in the STATUS
register
Eight bit objects are packed into each byte of memory storage, so they don’t consume large amounts of internal RAM.
Operations on bit objects are performed using the single bit instructions (bsf and bcf) wherever possible, thus the generated code to access bit objects is very efficient.
Arithmetic Operations
30
Operator Description Example
+ Addition a = b + c;
- Subtraction a = b – c;
+= Addition and assignment a += 16;
-= Subtraction and assignment b -= 32;
<< Shift left by ‘n’ bits a << 4;
>> Shift right by ‘n’ bits c >> 2;
& Logical AND a = b & c;
| Logical OR c = a | b;
29
30
2/27/2019
16
Computed Goto
31
• Remember the Notes #3, lookup table (LUT) of a Square function:
SQR: brwretlw d’0’ retlw d’1’ retlw d’4’retlw d’9’retlw d’16’retlw d’25’retlw d’36’retlw d’49’retlw d’64’retlw d’81’retlw d’100’
Computed Goto’s
32
• In C we use a ‘switch’ statement;
• So, the following function/subroutine would return the square for each number (0,…,10)
unsigned char Square(char num) {
switch(num) {
case 0: return(0);case 1: return(1);case 2: return(4);case 3: return(9);case 4: return(16);case 5: return(25);case 6: return(36);case 7: return(49);case 8: return(64);case 9: return(81);default: return(100);
}}
With the full version of the XC8 compiler (not free), this code will be optimized to a computed goto.
31
32
2/27/2019
17
Timing Loops
33
• Remember the 1-second delay loop:
• Now is:
DELAY: movlw d'167' ; this is the OUT_CNT starting value.movwf out_cnt ; store it in OUT_CNT register.
mid_agn movlw d'176' ; this is the MID_CNT starting value.movwf mid_cnt ; store it in MID_CNT register.
in_agn movlw d'10' ; this is the IN_CNT starting value.movwf in_cnt ; store it in IN_CNT location.
in_nxt decfsz in_cnt,f ; decrement the inner counter value.goto in_nxt ; if not zero, go decrement again.decfsz mid_cnt,f ; decrement the middle counter.goto in_agn ; if middle cntr is not zero, do inner again.decfsz out_cnt,f ; decrement the outer counter.goto mid_agn ; if outer cntr is not zero, do middle again.return ; if outer counter = 0 then done
// Delay for 1000ms = 1second__delay_ms(1000);
// oscillator frequency for delay#define _XTAL_FREQ 10000000
Must define the correct oscillator frequency:
two underscores, not one
Interrupts
34
• Can be easily enabled by using enable interrupt function: ei()
• Can be easily disabled by using diableinterrupt function: di()
ADIE = 1; // A/D interrupts will be usedPEIE = 1; // peripheral interrupts are enabledei(); // enable interrupts………di(); // disable interrupts
Like setting GIE = 0;
33
34
2/27/2019
18
Interrupts
35
// Interrupt-on-change example
void interrupt my_isr(void){
if(IOCIE && IOCBFbits.IOCBF4) {
// perform task required
IOCBFbits.IOCBF4 = 0;}
}
clear the flag
Use:
‘interrupt’ qualifier
Use of Mixed Code
36
• Various methods exist for mixing assembly code in with the C source code.
Use #asm and #endasm directives
unsigned int var;
void main(void){
var = 1;#asm
banksel (_var)rlf (_var), 1rlf (_var+1), 1
#endasm}
Selects the correct data memory bank for the variable
Suppose variable ‘var’ is at data memory location 0x74 (chosen by C compiler)
(_var) = memory location 0x74, (_var+1) is memory location 0x75
35
36
2/27/2019
19
Loops
37
int16_t ii;int8_t array[10];
for(ii=0;ii<10;ii++){
array[ii] = ii;……
}
Check MPLAB X IDE -> Window -> Output-> Disassembly Listing File
10-byte character array
When defining an array of characters, integers, etc., remember the size limitations of the data memory.
For example, int16_t array[100] represents 200 bytes, and will be filing up a large portion of your data memory.
Loops
38
bit flag;
…
flag = determineFlag()
while(flag){
………flag = determineFlag();…
}
bit flag;
…
…
do{
………flag = determineFlag();…
} while(flag);
37
38
2/27/2019
20
Infinite Loop
39
while(1){
……………
}
Condition is always true, program within loops keeps getting executed.
39