Learning PIC Microcontrollers
The table below lists specific items, components and or parts in relation to use or interfacing with various PIC microcontrollers. The remainder of this page gives general background information about using and programming PIC microcontrollers.
PIC Part Number | Component | Description | Data Sheet |
---|---|---|---|
PIC 16F876 | Timer0 Module | Demonstrate the use of the on-board Timer0 module | PIC16F876 |
PIC 16F876 | Capture/Compare/Pulse Width Modulation (CCP) | Demonstrate the use of the on-board CCP module for pulse width modulation (PWM) signal generation | PIC16F876 |
PIC 16F876 | Analog Digital Converter (ADC) | Demonstrate the use of the on-board ADC module for converting voltage to a digital quantity. | PIC16F876 |
PIC 16F876 | USART (RS232) Serial Comms Module | Demonstrate the use of the on-board Universal Synchronous Asynchronous Receiver Transmitter (USART) module for serial I/O (RS232) with a PC. | PIC16F876 | PIC 18F248 | In-Circuit Serial Programming (ICSP) - Flashing a LED | Demonstrate the minimum circuit for a PIC microcontroller - In-Circuit Serial Programming (ICSP) and flashing a LED. | PIC18F248 | PIC 18F248 | Software Button Debounce | Demonstrate debounce of hardware buttons using a software approach with PIC microcontroller and interrupts. | PIC18F248 |
PIC Microcontroller and C Programming
The following sections record some general information (and some specifics to CSS C compiler) about using and programming the PIC microcontroller. This is rather an 'eclectic' grouping of information, and more represents areas of difficulty in understanding I had over time, rather than an orderly approach to learning the PIC microcontroller and or C programming.
- Bit Masking
- Clock Oscillator and Instruction Cycle
- Pointer Variables/Arithmetic
- Serial Peripheral Interface (SPI) Modes
Bitmasking
Bit-masking is a programming/coding technique to selectively modify individual bits within a discrete 'chunk' of RAM or whatever (e.g. within a byte or bytes that are the locations of 'variables' or similar information) generally without affecting other bits from within the same 'chunk' (1). Remember, 'setting' a bit means that bit will have a value of '1', and 're-setting' a bit means a value of '0'. Eight 'bits' to a 'byte' etc.
Bit SET
To set a bit, use the OR operator, which is denoted by the | symbol. To set a bit, a memory location/variable is OR with a bit number, and the corresponding bit number in the memory location will be set (i.e. equal 1).
For example, there is a memory location called REGISTER, by performing the operation
REGISTER = REGISTER | 0x80;
REGISTER |= 0x80; //same as previous line - 'short hand' way
This would would cause bit number 7 within the variable REGISTER to be 'set' (i.e. have a value of 1). If REGISTER had the value of zero before the operation (i.e. binary 00000000), after the operation the value of REGISTER would be binary 10000000 (i.e. decimanl 255).
Bit RESET
To reset a bit, instead of using OR, the AND logical statement is used (the & symbol is used when writing the actual instructions). Again, for example, there is a memory location called REGISTER, by performing the operation
REGISTER = REGISTER & 0x7F; //0x7F = 0b01111111 = decimal 127
//the next instruction reset's bit 7 as-well
REGISTER = REGISTER & ~(0x80); //0x80 = 0b10000000 = decimal 128
The use of the tilde '~', which is the negation or 'not' operator, is perhaps easier in the above examples.
Bit checking
Suppose you want to wait for a register's bit#7 to set or reset:
while(!(REG & (1 << 7))); //waiting until bit 7 is set
while(REG & (1 << 7) != 0); //waiting until bit 7 is reset
Clock Oscillator and Instruction Cycle
The 'clock' frequency or crystal frequency and instruction cycle time can be a little confusing when first coming to grips with using micrcontrollers. At the most basic view, the operation of a micrcontroller (PIC, ARM, etc) is just a succession of instructions, each dependent on the previous instruction and whatever inputs from peripherals etc. Therefore, it is necessary to have a 'clock' signal to coordinate these instructions.
The overall speed of a microcontroller is absolutely dependent on this clock frequency. This clock frequency provides the timing for the CPU, but also for various counters and timers that in turn enable functions such as communications, ADC etc. The higher the clock frequency generally the higher the power consumption.
The microcontoller performs 'instructions' in succession ('coordinated' by the clock frequency), but each 'instruction' requires sub-steps (due to the way micrcontrollers actually physically operate at the circuit/transistor level). That is way the 'main' clock frequency (e.g with a PIC circuit, this is the 'crystal' frequency) is automatically sub-divided by the micrcontroller into a fixed lower frequency signal. This lower frequency signal becomes the 'machine cycle' or 'instruction cycle' that is actually the fundamental unit of action that a microcontroller can actually perform a 'physical' action (eg like loading a memory location etc). In the case of PIC microcontrollers, this is called the instruction cycle. In PIC 16 microcontrollers the 'crystal' frequency is divided by 4 to produce the instruction cycle time.
Clock Frequency ('crystal' frequency) | Instruction Cycle | |
---|---|---|
Frequency | Period | |
20 MHz | 5 MHz | 200 ns |
10 MHz | 2.5 MHz | 400 ns |
4 MHz | 1 MHz | 1 us |
32.768 kHz | 8.192 kHz | 122.1 us |
Pointer Variables in C/Arithmetic
Memory usage in microcontrollers is very important, as RAM is generally very limited (particularly in the "usual" PIC parts used by hobbyists). Pointers are the way in the C programming language of directly working with individual memory locations, and therefore are important to understand for utilising microcontrollers efficiently.`The "why" of using pointers in C is covered succinctly in (2). Whereas, "what" are pointers in C is explained well in (3). The following are my notes regarding pointers in C from these references, and some examples/practice using actual CCS C compiler code on a PIC18F248 (output via RS232 to PC serial terminal programme - see USART (RS232) Serial Comms Module for circuit and details).
PIC microcontroller RAM is a series of sequential locations, each with a numeric address starting at zero, that can store an 8-bit data item. Depending upon the exact PIC part in question, not all the RAM is usable as there may be special function registers taking up RAM.
A "variable" in C is just a "label" that is attached to a particular memory location, i.e., a descriptive label (to help the programmer) assigned to an address in RAM (and also has a specific type which tells the compiler the amount of RAM required). Such "normal" variables then store data at the particular RAM locations which represent integers (8-bit, 16-bit, 32-bit), floating-point, character (ASCII code) etc.
A "pointer" in C is "just" another variable, but a pointer variable stores the RAM address of another variable (not data that represents an interger or floating-point number etc).
A pointer variable is declared by giving a type (int, float, char etc) as for any other variable, but preceding the name of the pointer variable with an asterisk * which is called the indirection or dereferencing operator.
To get the RAM address of variable, which can then be stored in a pointer variable, the address operator (amperand &) is used.
The following CCS C code demonstrates the use of a pointer variable, with the output shown in the following figure:
void main() {
int8 myIntVar;
char myArray[] = "hello";
int8 *myIntPtr; //a pointer to a int variable
char *myArrayPtr; //a pointer to a char variable
myIntVar=99; //store the value 99 in myIntVar
myIntPtr = &myIntVar; //store the RAM address of myIntVar
myArrayPtr = myArray; //store the RAM address of the start of myArray
printf(" value of myIntVar = %u\n\r",myIntVar);
printf("address of myIntVar = 0x%x\n\r",&myIntVar); //print as hex value
printf(" value of myIntPtr = %Lu\n\r",myIntPtr);
printf("address of myIntPtr = 0x%x\n\r",&myIntPtr); //print as hex value
while(1);
}
The previous figure shows the contents of the file main.sym which the CCS C compiler produces showing the RAM addresses of the various variables used in the program. This clearly shows how a pointer variable contains the RAM address of another variable.
The unary operator * (i.e., the indirection operator) other than being used to define a pointer variable, is also used to return the value at the indicated memory location. It is not a direct memory access but rather an indirect access (hence the term indirection operator). So in the previous example code, if the statement prinf("value =%u\n\r",*myIntPtr); was included at the end, the output would have included the line value=99.
Serial Peripheral Interface (SPI) Modes
The Serial Peripheral Interface (SPI) bus is a synchronous serial communication interface specification used for short distance communication, primarily in embedded systems. The interface was developed by Motorola and has become a de facto standard.
The following table is useful for determining SPI mode parameters.
SPI Mode | Motorola | Microchip | CCS | Clock Line Idle | Data Clocked IN | ||
---|---|---|---|---|---|---|---|
CPOL | CPHA | CKP | CKE | ||||
0 | 0 | 0 | 0 | 1 | SPI_L_TO_H | SPI_XMIT_L_TO_H | Low | L to H |
1 | 0 | 1 | 0 | 0 | SPI_L_TO_H | Low | H to L |
2 | 1 | 0 | 1 | 1 | SPI_H_TO_L | High | H to L |
3 | 1 | 1 | 1 | 0 | SPI_H_TO_L | SPI_XMIT_L_TO_H | High | L to H |
- #define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H)
- #define SPI_MODE_1 (SPI_L_TO_H)
- #define SPI_MODE_2 (SPI_H_TO_L)
- #define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H)
References
- ref001: http://www.socialledge.com/sjsu/index.php?title=Bitmasking_Tutorial
- ref002: http://http://duramecho.com/ComputerInformation/WhyCPointers.html
- ref003: http://karwin.blogspot.com.au/2012/11/c-pointers-explained-really.html
Only Logged-In Members can add comments