10.0. PIC Software Development

 
    10.1. Mark 1.c (Test RS232 Communications)
    10.2. Mark 2.c (Fully TestRS232 Communications)
    10.3. Mark 3.c (Real-time Mode: 1 channel, fixed sampling delay)
    10.4. Mark 4.c (Test RS232, Baud-Rate Set by dip-switches)
    10.5. Mark 5.c (Fully testSR232 communications, adjustable baud-rate)
    10.6. Mark 6.c (Real-time mode: 1 channel, fixed delay, adjustable baud-rate)
    10.7. Mark 7.c (Test Timer Interrupts)
    10.8. Mark 8.c. (Real-time mode: dual channel, fixed delay, adjustable baud-rate)
    10.9. Mark 9.c (Real-time mode: four channels, chop, interrupt time-base)
    10.10. Mark 10.c (Test External RAM Chip)
    10.11. Mark 11.c (Main Program)
 

 
The philosophy used during the development of the PIC code was to keep it simple, straightforward, comprehensible, and to a minimum. There are many small programs designed for testing the hardware and ideas, each program is labelled mark 1, 2, 3, etc… The end result is that the PIC code is gradually built up step-by-step, instead of writing the entire program at once. This ensures that operational results are obtained, as testing producers are carried out at each stage, while if the program was written all at once, there is little chance it will work and could prove difficult to debug.
 
The high-level programming language C was chosen and not the low-level assembly code normally associated with PIC programming. There are many advantages for using C including: ease of programming, ease of modification, reusability of code, use of standard functions (e.g. printf, getc, putc, etc…), etc… But there is one drawback, C code is much less efficient, for example typically code produced by the C compiler (CCS) is at least twice as large as that programmed in assembly. Since the PIC16F877 has a large program store (8k) and runs at 20MHz this drawback is not a problem.
 

 
10.1. Mark 1.c (Test RS232 Communications)
 
This program is extremely simple; basically it initialises RS232 communications and transmits “Testing…” once a second (see figure 10.1a). This program was used to test RS232 communications (transmit mode), including MAX232, PIC UART and cable. The PIC is connected to a PC which is running the DOS-based test program in RX terminal mode to receive the incoming characters.
 
Figure 10.1a. Mark 1 flowchart
 
Mark 1.c source code: -
/* --------------------------------------------------------
   | FILE    : mark 1.c                                   |
   | PROJECT : Low Cost PC Based Oscilloscope             |
   | DESC    : Test RS232                                 |
   | ==================================================== |
   | DATE    : 19/02/2002                                 |
   | BY      : Colin K McCord                             |
   | VERSION : 1.0                                        |
   -------------------------------------------------------- */
 
#include <16F877.h>
#device PIC16F877 *=16 ADC=10
 
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock = 4000000)   // 4MHz clock, change this value if using different clock speed.
#use rs232(baud=9600, xmit = PIN_C6, rcv = PIN_C7, parity = N, bits = 8)
 
main()
{
        while(TRUE)
        {      
               /* Note printf could be used but this function is wasteful and will not be used */
               putc('T');     // Transmit T
               putc('e');     // Transmit e
               putc('s');     // Transmit s
               putc('t');     // Transmit t
               putc('i');     // Transmit i
               putc('n');     // Transmit n
               putc('g');     // Transmit g
               putc('.');     // Transmit .
               putc('.');     // Transmit .
               putc('.');     // Transmit .
 
               delay_ms(1000);        // Preset delay, repeat every second
        }
}
 
 


10.2. Mark 2.c (Fully Test RS232 Communications)
 
This program is extremely simple; basically it initialises RS232 communications and waits for an incoming character, once a character is received the character is transmitted, this process repeats forever (see figure 10.2a). This program is used to fully test RS232 communications, using the test program in TX terminal mode to transmit a message and then in RX terminal mode to receive a message. A more comprehensive test can be carried out using the test program in the loop-back test mode; if this test passes it is certain that RS232 communications are optimal.   
 
Figure 10.2a. Mark 2 flowchart
 
Mark 2.c source code: -
/* -------------------------------------------------------
| FILE :    mark_2.c                                    |
| PROJECT : Low Cost PC Based Oscilloscope              |
| DESC :    Test Serial Communications, waits for an    |
|           incoming char and transmits the char.       |
| ====================================================  |
| DATE : 19/02/2002                                     |
| BY : Colin K McCord                                   |
| VERSION : 1.0                                         |
-------------------------------------------------------- */
        
 
#include <16F877.h>
#device PIC16F877 *=16 ADC=10
 
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock = 4000000)
#use rs232(baud=9600, xmit = PIN_C6, rcv = PIN_C7, parity = N, bits = 8)
 
main()
{
     /* Note kbhit() can be used to check for incoming char before calling getc() */
     while(TRUE)
     {
           putc(getc()); // Wait for incoming char and transmit....
     }
 
}
 


10.3. Mark 3.c (Real-Time Mode: 1 Channel, Fixed Sampling Delay)
 
This program reads ADC channel AN0 and transmits the reading through RS232 using the real-time frame structure with a fixed sampling delay of 10mS (that’s a sample rate of 100Hz) before repeating (see figure 10.3a). This program is extremely useful, as it tests the ADC, the scope program, and the real-time frame structure. The sample rate is fixed; allow the sample delay can be manually modified (e.g. 1ms = 1000Hz) and the program recompiled and load into the PIC, hence testing of different sample rates is possible.
 
Figure 10.3a. Mark 3 flowchart
 
Mark 3.c source code: -
/* --------------------------------------------------------
   | FILE    : mark_3.c                                   |
   | PROJECT : Low Cost PC Based Oscilloscope             |
   | DESC    : Read CH1 ADC, transmit result through      |
   |         : RS232 using the real-time frame format.    |         
   | ==================================================== |
   | DATE    : 19/02/2002                                 |
   | BY      : Colin K McCord                             |
   | VERSION : 1.1                                        |
   -------------------------------------------------------- */
 
 
#include <16F877.h>
#device PIC16F877 *=16 ADC=10
 
// use #device adc = 10 to implement a 10-bit conversion,
// otherwise the default is 8-bits.
 
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock = 4000000)
#use rs232(baud=9600, xmit = PIN_C6, rcv = PIN_C7, parity = N, bits = 8)
 
 
main()
{
        // NOTE: by default in CCS all var's are unsigned
        long int adcValue;                    // 16-bit storage for ADC reading
        char adcHI,adcLO;                     // 8-bit storage for real-time frames.
 
        setup_adc_ports(A_ANALOG);            // RA0 - RA4 Analogue, RE0 - RE2 digital
        setup_adc(ADC_CLOCK_INTERNAL);        // Use internal ADC clock.
        set_adc_channel(0);
       
        delay_us(20);  // Delay for sampling cap to charge
 
        while(TRUE)
        {
               adcValue = read_adc(); // Get ADC reading
 
               /* Convert 16-bit adcValue to Real-time frame structure, CH1 */
               adcHI = (char)((adcValue >> 5)& 0x1f);       // 0|0|0|d9|d8|d7|d6|d5
               adcLO = (char)((adcValue & 0x1f)|0x80);      // 1|0|0|d4|d3|d2|d1|d0
                      
               putc(adcHI);   // Transmit Byte 1 (d9...d5)
               putc(adcLO);   // Transmit Byte 2 (d4...d0)
 
               delay_ms(10);  // Preset delay, repeat every 10ms
        }
}
 
 


10.4. Mark 4.c (Test RS232, Baud-Rate Set by Dip-Switches)
 
Same as mark 1.c with the addition of function SetBaudRate(), this function sets the UART baud-rate based on the positions of the DIP switches that are connected to port E. This program is used to test the reading of the DIP switches and RS232 communications at different baud rates. Note that the PIC must be running at 20MHz because the percentage error for 115Kbps is too large at slower clock speeds.
 
Mark 4.c source code: -
/* --------------------------------------------------------
   | FILE    : mark_4.c                                   |
   | PROJECT : Low Cost PC Based Oscilloscope             |
   | DESC    : Test RS232, baud rate set by dip switches. |
   | ==================================================== |
   | DATE    : 01/03/2002                                 |
   | BY      : Colin K McCord                             |
   | VERSION : 1.0                                        |
   -------------------------------------------------------- */
 
#include <16F877.h>
#device PIC16F877 *=16 ADC=10
 
#fuses HS,NOWDT,NOPROTECT,NOLVP
 
#byte   PORTE = 0x09   // PortE lives in File 9
 
/* Note 20MHz clock must be used for 115,000 bps, the % error is to large at slower speeds */
#use delay(clock = 20000000)  // 20MHz clock, change this value if using different clock speed.
#use rs232(baud=9600, xmit = PIN_C6, rcv = PIN_C7, parity = N, bits = 8)
 
#use    fast_io(E)             // Fast access to PortE (don't fiddle with TRISE)
 
/* Forward declaration of functions */
void SetBaudRate();
 
 
main()
{
        set_tris_e(0x17);      // TRISE = 00010111; RE2,RE1 and RE0 TTL Inputs
 
        SetBaudRate();
 
        while(TRUE)
        {      
               /* Note printf could be used but this function is wasteful and will not be used */
               putc('T');     // Transmit T
               putc('e');     // Transmit e
               putc('s');     // Transmit s
               putc('t');     // Transmit t
               putc('i');     // Transmit i
               putc('n');     // Transmit n
               putc('g');     // Transmit g
               putc('.');     // Transmit .
               putc('.');     // Transmit .
               putc('.');     // Transmit .
 
               delay_ms(1000);        // Preset delay, repeat every second
        }
}
 
 
void SetBaudRate()
{
        switch(PORTE & 0x07)   // Read dip switches and setup baud rate
        {
               case 0: set_uart_speed(4800);   break;
               case 1: set_uart_speed(9600);   break;
               case 2: set_uart_speed(14400);  break;
               case 3: set_uart_speed(19200);  break;
               case 4: set_uart_speed(32768);  break;
               case 5: set_uart_speed(38400);  break;
               case 6: set_uart_speed(57600);  break;
               case 7: set_uart_speed(115200); break;
        }
}
 

 
10.5. Mark 5.c (Fully Test RS232 Communications, Adjustable Baud-Rate)
 
Same as mark2.c with the addition of function SetBaudRate(), this function sets the UART baud-rate based on the positions of the DIP switches that are connected to port E. This program is used to fully test RS232 communications at different baud rates.
 
Mark 5.c source code: -
/* -------------------------------------------------------
  | FILE    : mark_5.c                                   |
  | PROJECT : Low Cost PC Based Oscilloscope             |
  | DESC    : Test Serial Communications, waits for an   |
  |           incoming char and transmits the char. Baud |
  |           rate set by dip switches.                  |
  | ==================================================== |
  | DATE    : 01/03/2002                                 |
  | BY      : Colin K McCord                             |
  | VERSION : 1.0                                        |
  -------------------------------------------------------- */      
 
 
#include <16F877.h>
#device PIC16F877 *=16 ADC=10
 
#fuses HS,NOWDT,NOPROTECT,NOLVP
 
#byte   PORTE = 0x09   // PortE lives in File 9
 
 
/* Note 20MHz clock must be used for 115,000 bps, the % error is to large at slower speeds */
#use delay(clock = 20000000)  // 20MHz clock.
#use rs232(baud=9600, xmit = PIN_C6, rcv = PIN_C7, parity = N, bits = 8)
 
/* Forward declaration of functions */
void SetBaudRate();
 
main()
{
        set_tris_e(0x17);      // TRISE = 00010111; RE2,RE1 and RE0 TTL Inputs
        SetBaudRate();
 
        /* Note kbhit() can be used to check for incoming char before calling getc() */
        while(TRUE)
        {
               putc(getc());  // Wait for incoming char and transmit....
        }
 
}
 
 
void SetBaudRate()
{
        switch(PORTE & 0x07)   // Read dip switches and setup baud rate
        {
               case 0: set_uart_speed(4800);   break;
               case 1: set_uart_speed(9600);   break;
               case 2: set_uart_speed(14400);  break;
                case 3: set_uart_speed(19200);   break;
               case 4: set_uart_speed(32768);  break;
               case 5: set_uart_speed(38400);  break;
               case 6: set_uart_speed(57600);  break;
               case 7: set_uart_speed(115200); break;
        }
}
 
 


10.6. Mark 6.c (Real-Time Mode: 1 Channel, Fixed Delay, Adjustable Baud-Rate)
 
Same as mark3.c with the addition of function SetBaudRate(), this function sets the UART baud-rate based on the positions of the DIP switches that are connected to port E. This program is used to test the real-time communications protocol and scope program at different baud rates.
 
 
Mark 6.c source code: -
/* --------------------------------------------------------
   | FILE    : mark_6.c                                  |
   | PROJECT : Low Cost PC Based Oscilloscope             |
   | DESC    : Read CH1 ADC, transmit result through      |
   |         : RS232 using the real-time frame format.    |
   |         : Baud rate set by dip-switches.             |
   | ==================================================== |
   | DATE    : 01/03/2002                                 |
   | BY      : Colin K McCord                             |
   | VERSION : 1.2                                        |
   -------------------------------------------------------- */
 
#include <16F877.h>
#device PIC16F877 *=16 ADC=10
 
// use #device adc = 10 to implement a 10-bit conversion,
// otherwise the default is 8-bits.
 
#fuses HS,NOWDT,NOPROTECT,NOLVP
 
#byte   PORTE = 0x09   // PortE lives in File 9
 
/* Note 20MHz clock must be used for 115,000 bps, the % error is to large at slower speeds */
#use delay(clock = 20000000)
#use rs232(baud=9600, xmit = PIN_C6, rcv = PIN_C7, parity = N, bits = 8)
 
#use    fast_io(E)             // Fast access to PortE (don't fiddle with TRISE)
 
/* Forward declaration of functions */
void SetBaudRate();
 
main()
{
        // NOTE: by default in CCS all var's are unsigned
        long int adcValue;                    // 16-bit storage for ADC reading
        char adcHI,adcLO;                      // 8-bit storage for real-time frames.
        set_tris_e(0x17);                      // TRISE = 00010111; RE2,RE1 and RE0 TTL Inputs
        SetBaudRate();
 
        setup_adc_ports(A_ANALOG);            // RA0 - RA4 Analogue, RE0 - RE2 digital
        setup_adc(ADC_CLOCK_INTERNAL);        // Use internal ADC clock.
        set_adc_channel(0);
       
        delay_us(20);  // Delay for sampling cap to charge
 
        while(TRUE)
        {
               adcValue = read_adc(); // Get ADC reading
 
               /* Convert 16-bit adcValue to Real-time frame structure, CH1 */
               adcHI = (char)((adcValue >> 5)& 0x1f);       // 0|0|0|d9|d8|d7|d6|d5
               adcLO = (char)((adcValue & 0x1f)|0x80);      // 1|0|0|d4|d3|d2|d1|d0
                      
               putc(adcHI);   // Transmit Byte 1 (d9...d5)
               putc(adcLO);   // Transmit Byte 2 (d4...d0)
 
               delay_ms(1);   // Preset delay, repeat every 1ms, that’s 1000 Hz
                              // baud rate must be at least 20000bps, try 32768bps.
        }
}
 
void SetBaudRate()
{
        switch(PORTE & 0x07)   // Read dip switches and setup baud rate
        {
               case 0: set_uart_speed(4800);   break;
               case 1: set_uart_speed(9600);   break;
               case 2: set_uart_speed(14400);  break;
                case 3: set_uart_speed(19200); break;
               case 4: set_uart_speed(32768);  break;
               case 5: set_uart_speed(38400);  break;
               case 6: set_uart_speed(57600);  break;
               case 7: set_uart_speed(115200); break;
        }
}
 
 

 
10.7. Mark 7.c (Test Timer Interrupts)
 
This program uses all of the PICs built-in timers, basically timers 0-2 are setup to cause an interrupt. An interrupt subroutine has been written for each timer; at preset intervals (0.5s, 0.25s, & 0.1s) a message is transmitted to the PC. The main program is stuck in a loop transmitting “main…” every second, Timer0_ISR transmits “Interrupt_T0…” every 0.5s, Timer1_ISR transmits “Interrupt_T1” every 0.25s and Timer2_ISR transmits “Interrupt_T2” 10-times a second. These messages are received using a PC running the DOS based test program in RX terminal mode, hence testing of the PIC timer interrupts including crude timing analysis is achieved, for example there should be two “Interrupt_T0…” messages between every “Main…”. See figure 10.7a for a simplified flowchart of the program.
 
 
Figure 10.7a.
Mark 7 flowchart
 
Mark 7.c source code: -
/* --------------------------------------------------------
   | FILE    : mark_7.c                                   |
   | PROJECT : Low Cost PC Based Oscilloscope             |
   | DESC    : Test timer interrupts                      |
   | ==================================================== |
   | DATE    : 05/03/2002                                 |
   | BY      : Colin K McCord                             |
   | VERSION : 1.1                                        |
   -------------------------------------------------------- */
 
#include <16F877.h>
#device PIC16F877 *=16 ADC=10
 
#fuses HS,NOWDT,NOPROTECT,NOLVP
 
#byte   PORTE = 0x09   // PortE lives in File 9
 
/* Note 20MHz clock must be used for 115,000 bps, the % error is to large at slower speeds */
#use delay(clock = 20000000)
#use rs232(baud=9600, xmit = PIN_C6, rcv = PIN_C7, parity = N, bits = 8)
 
#use    fast_io(E)             // Fast access to PortE (don't fiddle with TRISE)
 
#define T0_INTS_PER_SEC 76    // (20,000,000/(4*256*256))
#define T1_INTS_PER_SEC 76    // (20,000,000/(4*1*65536))
 
byte int_count0;              // Number of T0 interrupts left before a 0.5s has elapsed.
byte int_count1;              // Number of T1 interrupts left before a 0.25s has elapsed.
byte int_count2;              // Number of T2 interrupts left before a 100 msec has elapsed.
 
#int_rtcc                      // RTCC (timer0) interrupt, called every time RTCC overflows (255->0)
Timer0_ISR()          
{
        if(--int_count0==0)
        {
               printf("Interrupt_T0...");            // Every 0.5 seconds.
               int_count0 = T0_INTS_PER_SEC/2;
        }
}
 
#INT_TIMER1            // timer1 interrupt, called every time timer1 overflows (65536->0)
Timer1_ISR()          
{
        if(--int_count1==0)
        {
               printf("Interrupt_T1...");            // Every 0.25 seconds.
               int_count1 = T1_INTS_PER_SEC/4;
        }
}
 
#INT_TIMER2            // timer2 interrupt
Timer2_ISR()          
{
        if(--int_count2==0)
        {
               printf("Interrupt_T2...");            // Every 0.1 seconds.
               int_count2 = 100;
        }
}
 
/* Forward declaration of functions */
void SetBaudRate();
 
main()
{
        set_tris_e(0x17);      // TRISE = 00010111; RE2,RE1 and RE0 TTL Inputs
        SetBaudRate();
 
        /** Setup timer0 (RTCC) **/
        int_count0 = T0_INTS_PER_SEC/2;              // 0.5 seconds
        set_rtcc(0);
        setup_counters(RTCC_INTERNAL,RTCC_DIV_256);
        enable_interrupts(RTCC_ZERO);
 
        /** Setup timer1 **/
        int_count1 = T1_INTS_PER_SEC/4;              //0.25 second
        setup_timer_1(T1_DIV_BY_1 | T1_INTERNAL);   
        set_timer1(0);
        enable_interrupts(INT_TIMER1);
 
        /** Setup timer2 **/
        int_count2 = 100;                             // 0.1 second
        setup_timer_2 (T2_DIV_BY_4,125,9);            // interrupt every 1ms
        set_timer2(0);                              
        enable_interrupts(INT_TIMER2);
 
        enable_interrupts(GLOBAL);
 
        while(TRUE)
        {
               printf("Main...");
               delay_ms(1000);               // 1 second delay
        }
}
 
void SetBaudRate()
{
        switch(PORTE & 0x07)   // Read dip switches and setup baud rate
        {
               case 0: set_uart_speed(4800);   break;
               case 1: set_uart_speed(9600);   break;
               case 2: set_uart_speed(14400);  break;
               case 3: set_uart_speed(19200);   break;
               case 4: set_uart_speed(32768);  break;
               case 5: set_uart_speed(38400);  break;
               case 6: set_uart_speed(57600);  break;
               case 7: set_uart_speed(115200); break;
        }
}
 

 
10.8. Mark 8.c (Real-Time Mode: Dual Channel, Fixed Delay, Adjustable Baud-Rate)
 
Same as mark6.c expect that instead of just sampling channel 1 the program chops between channel 1 and channel 2 (see figure 10.8a). This program is used to test the scope program in dual trace mode, triggering methods and stability of a trace when the other is being used as the trigger. Note the configuration of the ADC has been modified so that 32Tosc is used and not the internal RC oscillator. The reason for this change is that the PIC16F877 datasheet states “When the device frequencies are greater than 1MHz, the RC A/D conversion clock source is only recommended for sleep operation”.
 
 
Figure 10.8a.
Mark 8 flowchart
<