Author Topic: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s  (Read 12283 times)

0 Members and 1 Guest are viewing this topic.

Offline caroper

  • Regular Contributor
  • *
  • Posts: 193
  • Country: za
    • Take your PIC
That is quite some title so lets begin by looking at what this topic actually covers and why.
  • Microchip XC8 - Is it worth it.
  • A free RS232 PC interface (No Transistors or Chips needed)
  • SoftUART - A Powerful debugging tool and new functionality for old PICS
  • Porting C Code - Traps for Young Players.
  • PICKit2 - Logic and UART Tools, a practical example of use.
When Microchip released the XC8 compiler I decided to give it a try out of curiosity. I had always considered, given the scarcity of memory (both Program Flash and RAM) and the lack of a hardware Stack Pointer, that the 8 bit PIC’s in the Baseline and Midrange families should remain the preserve of Assembler, whilst conversely, given the raw MIPS (pun intended) available in the PIC32 and dsPIC lines, C was the obvious choice if you need to get a project coded before the target Chip was obsolete.

I was under the impression (one that the Microchip web site tries its best to create) that in order to use XC8 I had to install MPLABX. As my critical PIC32 project was finally out of the door and had survived several months of field trials, I decided to bite the bullet and start porting all of my projects to MPLABX.  I tried, I really tried, but MPLABX and I just don’t see eye to eye. Given that I already need to use Visual Studio, AVR Suite, MPLAB 8.x and a several less obvious IDE’s for some more esoteric devices, I already have my fill of incompatible IDE’s. I don’t have the time nor patience to spend learning yet another bloated implementation. So given that MPLABX was a non starter, yet again, and that I didn't really need a C compiler for the 8 bit PIC’s I went back to doing some real work.

Fast forward to now and a friend asked for advice on getting started with PIC’s, so I showed him the excellent Tutorials at http://www.gooligum.com.au/. The same tutorials that had got me up to speed several years ago.  The site had been updated since my last visit so I decided to have a look at the “Mid-Range PIC C Programming Tutorials”. To my great delight, David Meiklejohn, the owner of Gooligum Electronics and a member of this board, had chosen to convert all of his Tutorials to XC8, and so a couple of fun filled hours later, thanks to David's readable, detailed and practical style of education, I was up and running with XC8 on MPLAB 8.x (YES MPLABX is NOT a prerequisite) and had sufficient knowledge of XC8 to be dangerous.  I now had a solution that was looking for a problem. My experiments courtesy of Gooligum Electronics had convinced me that C could work on the smaller PIC’s and that for many projects the overhead in code size was a reasonable compromise for speed and convenience when developing. What I needed now then was a suitable application to try it on.

In my PIC32MX Quickstart - A Tutorial for Hobbyists series I had promised to cover using the PICkit2 Logic and UART Tools, and as many of the smaller PIC chips don’t even have a UART I decided that a suitable project would be a Software defined (or Bit Banged) UART. XC8 could be used to provide portability between devices as well as the convenience of C for creating the code in a relatively easy to read format. Any project involving Serial Communications would be a great subject for using the PICkit tools too, so I could meet my early promise.  Rather than reinvent the wheel I dug around the net and found a suitable candidate at http://www.romanblack.com/ .
The result is Posted here: SoftUART - A free PIC debugging tool for the masses.

The rest of this article will look at the process of porting and modifying Roman’s code to the target Compiler and Hardware, as well as a look at how the PICkit2 Logic Analyser and UART tools were used to make the process easier. I didn’t even turn on the Oscilloscope or pull the ICD3 or my dedicated Logic and Protocol Analyzers out of the cupboard, just the oft neglected PICkit2 did the work of many hundreds of dollars worth of tools. And you end up with a portable piece of code that you will appreciate more the longer you play with and develop projects based on Microcontrollers (the same principals, and even the PICkit2 Logic and UART Tools will work just as well with AVR or any other family of microcontroller chip).

I will not be teaching you how to program in C, nor will I be explaining how to get XC8 running on the Midrange family. David has that covered at  http://www.gooligum.com.au/ look for the Mid-Range PIC C Programming Tutorials. You can probably get through those faster than you did this Introduction and I could never hope to get the subject across as well as he does.

As with my other Tutorials I will split this across several posts, so keep an eye on this thread if you want to own the best Free Debugging Tool around and at the same time pick up some, hopefully,  useful debugging and porting tips.

Cheers
Chris
« Last Edit: February 26, 2013, 12:02:05 pm by caroper »
 

Offline caroper

  • Regular Contributor
  • *
  • Posts: 193
  • Country: za
    • Take your PIC
Re: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s
« Reply #1 on: February 26, 2013, 12:14:22 pm »
Getting Started

This project is going to be developed using Microchips FREE XC8 C Compiler. You can find it HERE -
http://www.microchip.com/pagehandler/en_us/devtools/mplabxc/

If you need help installing it, hop on over tohttp://www.gooligum.com.au/ and Read Davids tutorial Lesson One - Basic I/O, found  in the Midrange PIC C Programing section. In fact go ahead and read all of them and do the examples you find there, it will get you up to speed with XC8 in record time.

Gather your tools.

For this tutorial I will be using the PICkit2 with the Low pin count demo board and a  PIC16F690 Midrange microcontroller, it is a great starter kit (and a useful dev board once you are up to speed - I will post a thread about hacking it later). It is also supported by David’s tutorials which beat the included Microchip versions hands down. You can get it all as a package HERE -
http://www.microchipdirect.com/productsearch.aspx?Keywords=DV164120

If you have a PICkit2 already  but no demo board I will post a separate entry with the necessary breadboard setup to get you started or alternatively whilst you are on Gooligum, order David’s demo board and Tutorial package.
http://www.gooligum.com.au/devboards/base-mid/base-mid.html

You will also need some Jumper wires and, if you intend to build the RS232 Adapter, a DB9 Female Connector, some scrap Strip Board (Viroboard), 3 Male header pins and 2 resistors (22K and 470R).

And finally you will need a Terminal Emulator. I have tried many and by far the best for working with Microcontrollers is RealTerm. I keep a copy in my Microchip Folder along with MPLAB and the PICkit tools. It gets used more frequently than my ICD3 and is available FREE from http://realterm.sourceforge.net/

The Source Code

I can’t claim all the credit for this  as my code is based on the work of Roman Black (http://www.romanblack.com/) one of my favorite and most esoteric of PIC code optomisers. Take a look at his page, it is both entertaining and inspirational.  Roman's code was part of a PIC18 project and introduced the concept of the Free Hardware RS232 connection (http://www.romanblack.com/bitbangserial.htm)

One of the great advantages of C is that it is “Theoretically” portable between devices and platforms. That can save you a lot of time in any project. In this case I knew that I wanted to create a Bit Banged serial port and a quick web search turned up several candidates. Roman’s Code, was to my mind, the best of the bunch (as it usually is with his code) and the Free Hardware RS232 connection was a bonus. So inspired by its potential, I converted Roman’s code from MikorC to XC8 for portability across the Microchip family, changed it to use Timer0 to reduce its hardware dependency to that supported by Baseline and Midrange PIC’s and adapted it to provide both Normal and Inverted RS232 signals, so that it is equally at home driving the PICkit Uart Tool, an FTDI cable, a MAX232 chip or Directly connected to a PC RS232 Port uesing Roman's Hacked DB9 Connector. This tutorial is about the process involved.

Hook up the hardware and fire up MPLAB - if you don't have the Low pin count demo board then use whatever you normally use for PIC development. Create a new Project and sort out your workspace to suit your personal preference. If you need help Gooligum has the info.

The first thing I wanted to try was to prove the portability of XC8 between Midrange Devices at the very least. I had posted my source code for a working version of SoftUART here: SoftUART - A free PIC debugging tool for the masses That version was for the PIC12F675 and my Low pin count demo board is populated with a PIC16F690. Compare the initialization sections of the two pieces of XC8 Code below:
Code: [Select]
//____________________________________________________
//
//   Soft UART Test for PIC12f675
//   Based on http://www.romanblack.com/bitbangserial.htm
//
//   C.A.Roper - 2013/02/23
//____________________________________________________

#include <xc.h>
#include <stdint.h>
//
// Configuration Fuses
//
#pragma config   FOSC   = INTRCIO      // Internal RC Osc 4Mhz I/O function on GP4 & GP5
#pragma config   MCLRE   = OFF         // GP3 avalable for input
#pragma config   CP      = OFF         // Code Protection Off
#pragma config   CPD      = OFF         // Data Protection Off
#pragma config   WDTE   = OFF         // Watchdog Timer Disabled
#pragma config   BOREN   = OFF         // Brownout Detection Disabled
#pragma config   PWRTE   = OFF         // Power-up Timer
//
// Prototypes of functions used
//
void send_serial_byte(unsigned char);
unsigned char receive_serial_byte(void);
//
//   Global Defines
//
#define _XTAL_FREQ       4000000         // System Clock rate
#define BAUDRATE      19200         // Desierd BAUD Rate (tested) Rates 9600 and 19200
#define SER_BAUD       ((_XTAL_FREQ / 4) / BAUDRATE - 3)
#define SER_BIT         0             // Signal MODE - 1 = Normal 0 = Inverted (Use Inverted for direct 232)
//
// I/O Pins
//
#define TxPin          GPIObits.GP0   // Output Tx 
#define RxPin          GPIObits.GP1   // Input  Rx 
//
//   MAIN
//
void main(void)
{
//
//   Setup - Run once
//
   //   Default I/O Setup
   //
   ANSEL       = 0;            // Disable Analog
   CMCON      = 0b00000111;      // Comparator OFF
   GPIO      = 0;            // Turn all Outputs OFF
   TRISIO      = 0b001010;         // INPUTS: GP1 = RxPin, GP3 = Button
   OPTION_REGbits.T0CS = 0;      // Select TMR0 in Timer Mode   
   OPTION_REGbits.PSA  = 1;      // Prescaler assigned to WDT
//
//   Main Loop - Run Forever
//


The above is the original code for the 12F675 all I had to change were the references to GPIO and the GPx Pins. Replacing them with PORTA and RAx Pin references. A Search and replace accomplished that in seconds.  I also had to remove the line :

CMCON = 0b00000111;      // Comparator OFF

There is no such Register in the PIC16F690, Finaly I changed  TRISIO to TRISA and that was it, the code compiled and ran first time.

Code: [Select]
//____________________________________________________
//
//   Soft UART Test for PIC16f690
//   Based on http://www.romanblack.com/bitbangserial.htm
//
//   C.A.Roper - 2013/02/26
//____________________________________________________

#include <xc.h>
#include <stdint.h>
//
// Configuration Fuses
//
#pragma config   FOSC   = INTRCIO      // Internal RC Osc 4Mhz I/O function on RA4 & RA5
#pragma config   MCLRE   = OFF         // RA3 avalable for input
#pragma config   CP      = OFF         // Code Protection Off
#pragma config   CPD      = OFF         // Data Protection Off
#pragma config   WDTE   = OFF         // Watchdog Timer Disabled
#pragma config   BOREN   = OFF         // Brownout Detection Disabled
#pragma config   PWRTE   = OFF         // Power-up Timer
//
// Prototypes of functions used
//
void send_serial_byte(unsigned char);
unsigned char receive_serial_byte(void);
//
//   Global Defines
//
#define _XTAL_FREQ       4000000         // System Clock rate
#define BAUDRATE      19200         // Desierd BAUD Rate (tested) Rates 9600 and 19200
#define SER_BAUD       ((_XTAL_FREQ / 4) / BAUDRATE - 3)
#define SER_BIT         0             // Signal MODE - 1 = Normal 0 = Inverted (Use Inverted for direct 232)
//
// I/O Pins
//
#define TxPin          PORTAbits.RA0   // Output Tx 
#define RxPin          PORTAbits.RA1   // Input  Rx 
//
//   MAIN
//
void main(void)
{
//
//   Setup - Run once
//
   //   Default I/O Setup
   //
   ANSEL       = 0;            // Disable Analog
   PORTA      = 0;            // Turn all Outputs OFF
   TRISA      = 0b001010;         // INPUTS: RA1 = RxPin, RA3 = Button
   OPTION_REGbits.T0CS = 0;      // Select TMR0 in Timer Mode   
   OPTION_REGbits.PSA  = 1;      // Prescaler assigned to WDT
//
//   Main Loop - Run Forever
//

You don’t get much more Portable than that, so hats off to Microchip, the project has passed the first portability test with flying colors and XC8 is justified in my mind at least.


Here is a screen capture of PICkit and RealTerm showing the result:




and photo of the Test Setup showing the PICkit2, The RS232 interface and my slightly hacked Demo board.



We won't be using anything to the right of the CPU so don't worry about having to Hack your demo board just yet. The crocodile clip is just an earth connection for the RS232 Port.

Up Next - Porting Roman's code fro MikorC to XC8.


Cheers
Chris


« Last Edit: February 26, 2013, 12:54:22 pm by caroper »
 

Offline caroper

  • Regular Contributor
  • *
  • Posts: 193
  • Country: za
    • Take your PIC
Re: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s
« Reply #2 on: February 26, 2013, 01:41:42 pm »
Not all Compilers are created equal

One of the oft stated reasons for working in C is code portability. As long as the C compiler you use conforms to the ANSI Standard you should be able to take a piece of C code written for one platform and recompile it in a version of ANCI C that supports an entirely different platform. A noble Ideal indeed.

The problem with Standards, however, is that there are so many choose from and the Compiler Vendors like to pick and choose which standard to support. Just to make things worse they also have a tendency to “Enhance” the standard.  To my mind an “Enhanced Standard” is equivalent to saying “Proprietary Compiler”, the noble goal of portability gets thrown out of the window in the process.

Lets have a look at converting Roman’s Code, beginning with the Send_byte function, as that is the most useful on a memory constrained devices as well as the easiest way to test the timing.

Code: [Select]
// a bit banged serial function, sends INVERTED serial data
// to PC serial port using just 2 resistors.

#define PIN_SER_OUT LATA.F3       // which pin for serial out (PORTA.F3)
#define SER_BAUD 51               // TMR1 (1Mhz/19200 baud) = 52
                                  // tested; works from 49 to 53, using 51

//---------------------------------------------------------
void send_serial_byte(unsigned char data)
{
  // this manually sends a serial byte out any PIC pin.
  // NOTE! serial is inverted to connect direct to PC serial port.
  // baud timing is done by using TMR1L and removing
  // timer error after each baud.
  unsigned char i;

  i=8;                            // 8 data bits to send

  PIN_SER_OUT = 1;                // make start bit
  TMR1L = (256 - SER_BAUD);       // load TMR1 value for first baud;
  while(TMR1L.F7);                // wait for baud

  while(i)                        // send 8 serial bits, LSB first
  {
    if(data.F0) PIN_SER_OUT = 0;  // invert and send data bit
    else    PIN_SER_OUT = 1;

    data = (data >> 1);           // rotate right to get next bit
    i--;
    TMR1L -= SER_BAUD;            // load corrected baud value
    while(TMR1L.F7);              // wait for baud
  }

  PIN_SER_OUT = 0;                // make stop bit
  TMR1L -= SER_BAUD;              // wait a couple of baud for safety
  while(TMR3L.F7);
  TMR1L -= SER_BAUD;
  while(TMR1L.F7);
}
//---------------------------------------------------------

The first deviation from that Standard that you notice in this code is the line:

      while(TMR1L.F7);              // wait for baud

You will have come across a similar construct whilst doing the Gooligum tutorials, it is a Union, and is probably defined as part of the compilers headers.

Roman chose to compile his Code in MikroC, and fortunately the people at MikroElektronica were kind enough to publish documentation about their compilers online.  Well almost, I couldn't find it by visiting the web page so I did a Google Search for MikroC Documentation and found this:

http://www.mikroe.com/pdf/mikroc/mikroc_manual.pdf

Unfortunately jumping through hoops to get Compiler Specific Information is one of the biggest obstacles when porting C code. In this case it was relatively easy, some Vendors actually want you to sign up and at least install the demo compiler before you get at the documentation.


Take a look at page 34 - Diversions from the ANCI C standard

You will need to find the equivalent for any Source Code that you wish to port and within that section we find:

        Accessing Individual Bits

with the footnote:

        "if aiming at portability, avoid this style of accessing individual bits and use the bit fields instead."

So at least we know we can bypass it by writing it as:

       while(TMR1L & 1<<7 );              // wait for baud

As you get more proficient at coding in C you will begin to recognize such situations and instinctively know how to work around them, but I make a point of it here as it is a major "Trap for Young Players".


Taking Roman's Code and plugging it into our own application we get this:

Code: [Select]

//____________________________________________________
//
//   Soft UART Test for PIC16f690
//   Based on http://www.romanblack.com/bitbangserial.htm
//
//   C.A.Roper - 2013/02/26
//____________________________________________________


#include <xc.h>
#include <stdint.h>
//
// Configuration Fuses
//
#pragma config   FOSC   = INTRCIO      // Internal RC Osc 4Mhz I/O function on RA4 & RA5
#pragma config   MCLRE   = ON         // RA3 NOT avalable for input
#pragma config   CP      = OFF         // Code Protection Off
#pragma config   CPD      = OFF         // Data Protection Off
#pragma config   WDTE   = OFF         // Watchdog Timer Disabled
#pragma config   BOREN   = OFF         // Brownout Detection Disabled
#pragma config   PWRTE   = OFF         // Power-up Timer
//
// Prototypes of functions used
//
void send_serial_byte(unsigned char);
//
//   Global Defines
//
// #define PIN_SER_OUT LATA.F3          // which pin for serial out (PORTA.F3)
#define PIN_SER_OUT PORTAbits.RA0       // which pin for serial out (PORTAbits.RA0)
#define SER_BAUD 51                     // TMR1 (1Mhz/19200 baud) = 52
//
//   MAIN
//
void main(void)
{
//
//   Setup - Run once
//
   //   Default I/O Setup
   //
   ANSEL             = 0;            // Disable Analog
   PORTA            = 0;            // Turn all Outputs OFF
   TRISA            = 0b001010;         // INPUTS: RA1 = RxPin, RA3 = Button
   T1CONbits.TMR1ON   = 1;            // Activate Timer 1


   send_serial_byte('U');       
 
//
//   Main Loop - Run Forever
//   
   while(1)
   {   
      ;
   }
}


//
//   User Functions
//


//---------------------------------------------------------
void send_serial_byte(unsigned char data)
{
  // this manually sends a serial byte out any PIC pin.
  // NOTE! serial is inverted to connect direct to PC serial port.
  // baud timing is done by using TMR1L and removing
  // timer error after each baud.
  unsigned char i;


  i=8;                            // 8 data bits to send


  PIN_SER_OUT = 1;                // make start bit
  TMR1L = (256 - SER_BAUD);       // load TMR1 value for first baud;
  while(TMR1L & 1<<7);                // wait for baud


  while(i)                        // send 8 serial bits, LSB first
  {
//    if(data.F0) PIN_SER_OUT = 0;  // invert and send data bit
    if(data & 1<<0) PIN_SER_OUT = 0;  // invert and send data bit
    else    PIN_SER_OUT = 1;


    data = (data >> 1);           // rotate right to get next bit
    i--;
    TMR1L -= SER_BAUD;            // load corrected baud value
    while(TMR1L & 1<<7);              // wait for baud
  }


  PIN_SER_OUT = 0;                // make stop bit
  TMR1L -= SER_BAUD;              // wait a couple of baud for safety
  while(TMR1L & 1<<7);
  TMR1L -= SER_BAUD;
  while(TMR1L & 1<<7);
}
//---------------------------------------------------------

I like to keep the user functions after main() so to avoid compiler errors I include a prototype of the functions at the top of the source as in:

//Prototypes of functions used
void send_serial_byte(unsigned char);

then we have the global defines, note that I have changed the pin definition to RA0 as that pin is connected to our PICkit UART tool. I doubles as the PGD line for ICSP and as such it makes sense to use it as our debug output. You can always reallocate the Pins after the code is working and it allows us to use the PICkit Logic Analyser without having to keep unplugging the programmer.

During setup we need to activate timer 1, and as we are not using a prescaler we can accept the default settings and just turn it on:

  T1CONbits.TMR1ON   = 1;            // Activate Timer 1

I like to load the header file for the target device as part of the project, it makes it easy to search for register names, unions and Structurs. It is not needed by the Compiler but is conveniant for the software developer. It can be found under \Program Files\microchip\XC8\include.


For the sake of the first test we are going to send a single character, so it can go in the setup portion, the main loop remains blank.

  send_serial_byte('U');       

Then we have Roman's actual function. Note that I have used search and replace to convert all instances of TMR1L.F7 to read TMR1L & 1<<7.

One final change that was needed also related to the same bit addressing is the line:

    if(data.F0) PIN_SER_OUT = 0;  // invert and send data bit

which becomes:

    if(data & 1<<0) PIN_SER_OUT = 0;  // invert and send data bit

If you have got everything correct as above it should compile without errors (you may get a Compiler Warning that is just Spam trying to get you to fork out US$500 for the Compiler Optimisation, I don't think it's worth it but you may decide otherwise.

Load the code into the PICKit and write the file.

Now to test the results.

In the PICkit2 program select Tools->Logic Tool...
Set Ch 1 = / to triger on a rising edge
Set the Sample Rate to 500Khz - 2ms Window
Press the run Button

Now press the Reset Button on the Demo Board.

You should capture a nice square wave (That is why I sent a 'U" look up its ASCII Code) with the First pulse slightly stretched, that one is the Start Bit, the rest of the Bit should be identical, or reasonably close seeing as we are only using an RC oscillator.



You can play with it for a while, use the cursors to measure time and frequency etc. Mine shows a Bit length of  52uS  and a Frequency of 9615.28Hz

If your PC has a Serial Port, now is a good time to make up the RS232 Header plug, that way you can also see the 'U" displayed in RealTerm and the waveform in the Logic Analyser. And as we are developing this code to serve as a Debugger, why not press it to work debugging itself as we build it :)

Details of construction are posted later in this thread here and you can use my Photo above for reference.  Just be careful to make sure you have the resistors in place and no Shorts. -12V on the input of your PICKit2 is guaranteed to fry it. You have been warned, work carefully.

Cheers
Chris
« Last Edit: February 27, 2013, 01:23:37 pm by caroper »
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 19686
  • Country: nl
    • NCT Developments
Re: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s
« Reply #3 on: February 26, 2013, 05:20:08 pm »
Not all Compilers are created equal

One of the oft stated reasons for working in C is code portability. As long as the C compiler you use conforms to the ANSI Standard you should be able to take a piece of C code written for one platform and recompile it in a version of ANCI C that supports an entirely different platform.
Unfortunately there are some limits. In my experience with 8 bit PICs (and 8051 and other microcontrollers/DSPs with seperate code and data memory) it is impossible to write genuine portable code because 8 bit PICs  can't deal with pointers very well. If you use pointers the code gets painfully slow and bloated. This doesn't mean you shouldn't use C on an 8 bit PIC. Just don't expect your code to be portable.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline caroper

  • Regular Contributor
  • *
  • Posts: 193
  • Country: za
    • Take your PIC
Re: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s
« Reply #4 on: February 26, 2013, 08:06:36 pm »
Making send_serial_byte() even more useful

Now that our SoftUART can send data to a PC we already have the basis of a very useful debugging function.  If you sprinkle the code you are trying to debug with with send_serial_byte() function calls, you can use RealTerm to track and know exactly where your code is executing as it goes.

You will notice that RealTerm has a useful capture Tab. When testing code that may only execute a certain function infrequently, or that you need to see working over a period of a Day or more, you can set RealTerm to capture any bytes sent by your Microcontroller and save them to a file. You can even set up the length of time to capture for or the maximum number of bytes. Coupled with the SoftUART, even as it stands with only the Send functionality, you have a tool that is perfect for debugging Home Automation or Data Logging type applications.

Not all PC’s
, however, have RS232 Ports these days and I have not tested this code with an RS232 to USB adapter cable (it should work though) so for those situations it would be nice to have the SoftUART able to send data to our PICkit UART Tool (which can also capture to a file) or to RealTerm via an FTDI or MAX232 chip.


If you close the Logic Tool and open the UART tool as it stands, you will see nothing displayed. That is because the send_serial_byte() function is using negative or inverted logic. The reason for this is that a MAX232 or equivalent circuit normally performs the signal inversion at the same time as it converts the voltage levels to RS232. Take a look at the schematic of a MAX232 chip and you will see why:





What we need to do then is provide an option to have our code run either Normal or Inverted Logic Signals. Not only will this give us the flexibility needed to talk to different devices with different requirement, it will actually give us one up on the Hardware UARTs built into many Microcontrollers as they are not capable of providing the inverted signals at all.

We will begin by defining a new constant that represents the Logic Level to use:

#define SER_BIT         0             // Signal MODE - 1 = Normal 0 = Inverted (Use Inverted for direct 232)


We also need a new line of code in our setup, because we have been using negative logic it didn't matter that the Tx Pin was sitting in the default low state, as that is the correct Idle State with negative logic. But if we wish to use positive logic, we need to pull that line high when we are not sending. This will take care of it:



   PIN_SER_OUT         = SER_BIT;

Then in our send_serial_byte() function we replace every occurrence of an explicit Bit State with our Constant. That way just by changing the one line of code at compile time we can invert the Logic of the entire Function. Four lines need to change:


  PIN_SER_OUT = !SER_BIT;         // make start bit


The Start bit is always the opposite of the Idle level so it is proceeded with a ! (NOT), and our two lines that send the actual data become:


    if(data & 1<<0) PIN_SER_OUT = SER_BIT;  // send data bit
    else    PIN_SER_OUT = !SER_BIT;

Finally we need to send a Stop Bit:



  PIN_SER_OUT = SER_BIT;          // make stop bit


In order to test it, however, we need to change our Test Character. the 'U' will be the same in both Positive and Negative logic, so change the test character to 'A'.

Our code the will look like this:


Code: [Select]

//____________________________________________________
//
//   Soft UART Test for PIC16f690
//   Based on http://www.romanblack.com/bitbangserial.htm
//
//   C.A.Roper - 2013/02/26
//____________________________________________________


#include <xc.h>
#include <stdint.h>
//
// Configuration Fuses
//
#pragma config   FOSC   = INTRCIO      // Internal RC Osc 4Mhz I/O function on RA4 & RA5
#pragma config   MCLRE   = ON         // RA3 NOT avalable for input
#pragma config   CP      = OFF         // Code Protection Off
#pragma config   CPD      = OFF         // Data Protection Off
#pragma config   WDTE   = OFF         // Watchdog Timer Disabled
#pragma config   BOREN   = OFF         // Brownout Detection Disabled
#pragma config   PWRTE   = OFF         // Power-up Timer
//
// Prototypes of functions used
//
void send_serial_byte(unsigned char);
//
//   Global Defines
//
// #define PIN_SER_OUT LATA.F3          // which pin for serial out (PORTA.F3)
#define PIN_SER_OUT PORTAbits.RA0       // which pin for serial out (PORTAbits.RA1)
#define SER_BAUD 51                     // TMR1 (1Mhz/19200 baud) = 52
#define SER_BIT         0             // Signal MODE - 1 = Normal 0 = Inverted (Use Inverted for direct 232)
//
//   MAIN
//
void main(void)
{
//
//   Setup - Run once
//
   //   Default I/O Setup
   //
   ANSEL             = 0;            // Disable Analog
   PORTA            = 0;            // Turn all Outputs OFF
   TRISA            = 0b001010;         // INPUTS: RA1 = RxPin, RA3 = Button
   T1CONbits.TMR1ON   = 1;            // Activate Timer 1

   PIN_SER_OUT         = SER_BIT;


   send_serial_byte('A');         
//
//   Main Loop - Run Forever
//   
   while(1)
   {   
      ;
   }
}


//
//   User Functions
//


//---------------------------------------------------------
void send_serial_byte(unsigned char data)
{
  // this manually sends a serial byte out any PIC pin.
  // NOTE! serial is inverted to connect direct to PC serial port.
  // baud timing is done by using TMR1L and removing
  // timer error after each baud.
  unsigned char i;


  i=8;                            // 8 data bits to send


  PIN_SER_OUT = !SER_BIT;         // make start bit
  TMR1L = (256 - SER_BAUD);       // load TMR1 value for first baud;
  while(TMR1L & 1<<7);                // wait for baud


  while(i)                        // send 8 serial bits, LSB first
  {
    if(data & 1<<0) PIN_SER_OUT = SER_BIT;  // send data bit
    else    PIN_SER_OUT = !SER_BIT;


    data = (data >> 1);           // rotate right to get next bit
    i--;
    TMR1L -= SER_BAUD;            // load corrected baud value
    while(TMR1L & 1<<7);              // wait for baud
  }



  PIN_SER_OUT = SER_BIT;          // make stop bit

  TMR1L -= SER_BAUD;              // wait a couple of baud for safety
  while(TMR1L & 1<<7);
  TMR1L -= SER_BAUD;
  while(TMR1L & 1<<7);
}
//---------------------------------------------------------


Switch back to the Logic Tool and press the Reset Button to capture the new waveform. If you still have RealTerm running you should see an 'A' displayed there too. My waveform looks like this:







Now Change the SER_BIT Constant to a 1 and recompile it.

You should capture a waveform like this:



Now switch to the UART Tool, Select 19200 as the speed and click Connect.


Press the Reset button and you should get an 'A' displayed.





You now have a Software UART that can send debug messages to either your PC, with or without a MAX232 or FTDI Chip, or directly to the PICkit2 UART Tool.

Your Debugging Tool Kit has just been expanded, and I think you will find it to be the most useful debugging Tool you ever had for the  Midrange PICs and it enhances your investment in the PICkit2 as well.


That is it for now and, apparently, I will have no power for the entire day tomorrow, so don't expect any updates for the Next 24 Hours.
When we continue we will be looking at implementing the receive_serial_byte() function.

Until then I will be sitting in the Sunshine, Reading an Electronics Magazine or two and drinking a couple of cold beers :) Enjoy your Wednesday everyone, I know I will enjoy mine out here in "Darkest Africa".


Cheers
Chris
« Last Edit: February 26, 2013, 10:47:39 pm by caroper »
 

Offline caroper

  • Regular Contributor
  • *
  • Posts: 193
  • Country: za
    • Take your PIC
Re: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s
« Reply #5 on: February 27, 2013, 01:04:20 pm »

Power up the Soldering Iron - we need a brake from Coding

To properly see what is going on whilst we experiment with the Rx portion of this code we are going to need a Terminal and a Logic Analyzer connected at the same time. This is so that we can send a Character to the Chip and Simultaneously see its waveform and Timing.


Unfortunately, unless you are lucky enough to have two PICkit2 devices, you will need an FTDI adapter or, even better if your PC has an RS232 Port, this RS232 Adapter Board. I will be using this board in the examples, as testing the concept of a free RS232 adapter board was one of the main driving forces of this project, and so here are a couple of Drawings to help you construct one.

 


The Circuit is so simple it does't require a schematic diagram, the two drawings above should give even a novice enough information to build one. A couple of notes are in order, however, to ensure that it is correctly wired, and to avoid any dangerous voltage levels on your Microcontroller or PICkit2 pins. (That is danger to the Electronics, not the user, we are only talking a maximum of plus or minus 12V with respect to Ground).

The Viroboard (Stripboard) is inserted between the two rows of Pins of the DB9 Female Socket, with the row of five Pins aligned with, and soldered to, the the five strips on the copper side of the board. Only three of the Pins will actually be used, but I soldered all five to get greater mechanical strength. Non of the Pins on the Component side of the Board are needed.

The Board has two sets of header pins mounted on the component side of the Board. The Single Pin header is the Ground Connection and the Double Pin Header are the Rx and Tx Pins. They are spaced such that the Board can be plugged into either the top or bottom of a Breadboard and connect to either Ground Bus. If you plug it into a breadboard ensure that the Ground Pin plugs into the Blue (Gnd) Bus and the Rx/Tx pins will plug into two rows on the breadboard.  When using it like this please ensure that the Ground pin is actually in the Correct bus before connecting an RS232 Cable, if it is in the wrong bus you will short out any USB device, such as the PICkit2, that you have connected.

To use it with the Low Pin Count Demo Board, plug the two pin header into the female header on the board such that the pins are in sockets 7 and 8 and use a clip lead or jumper wire to connect the ground to socket 14 (the last one).

Cheers
Chris

p.s.

I have tidied up the code developed so far, as the combination of Roman's style and mine was starting to get messy.

I have also renamed PIN_SER_OUT to TxPin and included a full Greeting Message rather than a single Character.
To capture the full message in the PICKit2 Logic Tool set the Sample Rate to 100 kHz - 10 ms Window.




The new version is attached below and will serve as a test for the the board described in this post.
It will also form the basis of Part 2 of this tutorial. The Receive Function.
« Last Edit: February 27, 2013, 09:18:39 pm by caroper »
 

Offline caroper

  • Regular Contributor
  • *
  • Posts: 193
  • Country: za
    • Take your PIC
Re: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s
« Reply #6 on: February 28, 2013, 08:07:17 am »
Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s - Part 2

So far we have developed code that can send an occasional byte (or string even, but that is highly unlikely on baseline PIC’s due to resource limitations) to any available PIN. This is probably the oldest and most popular debugging tool around, as it predates LED’s. Even the earliest of computers had TTY (Serial) ports available.  I recall debugging my work by watching a Teletype printer spit out obscure characters indicating to me which piece of code was currently executing. We also have a neat little adapter board that we can use to add a physical RS232 connection whilst debugging.

In the process the first half of this Tutorial has looked at the XC8 compiler and concluded that it is definitely a lot more useful on the smaller PIC’s than I had allowed for previously, in fact I fear my Assembler skills will be in danger of getting rusty from now on (though assembler is probably still required for creating library functions).

We looked at portability of code between Vendors and also within the Midrange family and found it to be relatively painless. As a result we have a working Serial Port interface for PIC’s that do not have sufficient UART’s, one that is flexible enough that it can be implemented even if no MAX232 or FTDI chip is available in the hardware.

We have also introduced the often overlooked capabilities of the PICkit2 Logic and UART tools.

Put on your hard hats, we are entering rough terrain.

Because it is a Tutorial I have kept it light hearted, though possibly a bit too verbose at times, and presented all of the code in a working form.  The Idea being to introduce the tools and to provide a useful piece of code without going into too many technical details and scaring off any “Young Players”.

Now we are going to delve into the Belly of the Beast. I am going to up the level slightly and start looking at the some of the more technical aspects of RS232 and we are going to be using the PICkit2 logic tools to both examine and test our theories.

As mentioned in the last post we will need access to both a Terminal and to the PICkit2 from now on, so if you have not made, or do not intend to make, the Serial Adapter Board you will need to use an FTDI board and talk via USB.  I use the AdaFruit FTDI friend but Sparkfun also has a version (with similar pinouts I believe) and several others can be found all over the Internet. I will leave it up to you to set up. Just connect the Ground and the Rx/Tx lines to the male header on the Low Pin Count Board or whatever you are using for these experiments.

Now back to the coding ...

Here is Roaman's Serial Receive function in its raw form:

Code: [Select]

void receive_serial_byte(void)
{
  // this manually receives a serial byte in any PIC pin.
  // NOTE! serial is inverted to connect direct to PC serial port.
  // baud timing is done by using TMR1L and removing
  // timer error after each baud. Starts with 1.5 baud delay,
  // so each bit is sampled in middle of baud.
  unsigned char i;


  i=8;                            // 8 data bits to receive


  TMR1L = (256 - SER_BAUD - 19);  // load TMR1 value for ~1.5 baud
  while(TMR1L.F7);                // wait for baud


  while(i)                        // receive 8 serial bits, LSB first
  {
    rdata = (rdata >> 1);         // rotate right to store each bit
    if(PIN_SER_IN)  rdata.F7 = 0; // invert and save data bit
    else      rdata.F7 = 1;


    i--;
    TMR1L -= SER_BAUD;            // load corrected baud value
    while(TMR1L.F7);              // wait for baud
  }


  TMR1L -= SER_BAUD;              // wait for stop bit, ensure serial port is free
  while(TMR1L.F7);


}

All we need to do to get it to compile in XC8 is the change the bit addressing, as again he has used the propitiatory MikroC ANSI C Enhancement. A Search and Replace will convert all instances of TMR1L.F7 into TMR1L & 1<<7. We also need to change rdata.F7 in a similar manner but to conform with the coding style I normally use it becomes RxChr & 1<<7. PIN_SER_IN becomes RxPin and as most of his opening comment is or will become redundant in this implementation I removed it.

Possibly the largest change is that Roman declared his function as void and used a Global variable to acess the result. My preference is to have the Function return the result and so I have declared the function as unsigned char and have declared a local variable unsigned char RxChr; that is returned to a calling function by the line return RxChr;

Here is the result:

Code: [Select]

unsigned char receive_serial_byte(void)
{
   
   unsigned char RxChr;            // holds the serial byte that was received
   unsigned char i;               // Bit Index
   i = 8;                        // 8 data bits to receive
   
   TMR1L =(256 - (SER_BAUD - 19));      // load TMR1 value to offset ~center of RxBit
   while(TMR1L & 1<<7);               // wait for baud
   while(i)                     // receive 8 serial bits, LSB first
   {
      RxChr = (RxChr>>1);             // rotate right to store each bit
     
      if(  RxPin == 1)            // save data bit
          RxChr = RxChr | SER_BIT<<7;
      else RxChr = RxChr |!SER_BIT<<7;   
     
      i--;                     // Next Bit
      TMR1L -= SER_BAUD;            // load corrected baud value
      while(TMR1L & 1<<7);            // wait for baud
   }
   
   TMR1L -= SER_BAUD;               // wait for stop bit, ensure serial port is free
   while(TMR1L & 1<<7);
   return RxChr;
}

In order to accommodate the new Function the RxPin has to be added to our existing code too. Our Global Defines become:

Code: [Select]

#define SER_BAUD       51            // TMR1 (1Mhz/19200 baud) = 52
#define SER_BIT         0                // Signal MODE - 1 = Normal 0 = Inverted (Use Inverted for direct 232)
//
#define TxPin         PORTAbits.RA0   // which pin for serial out
#define RxPin         PORTAbits.RA1   // which pin for serial in

and our main loop now gets populated:

Code: [Select]

//
//   Main Loop - Run Forever
//
   while(1)
   {   
      if(RxPin == !SER_BIT)      // wait here for serial byte start bit
      {
         send_serial_byte(receive_serial_byte());   // Echo back     
      }   
   }

and because we added a new Function we need to provide a prototype to avoid compiler errors:

Code: [Select]

//
// Prototypes of functions used
//
void send_serial_byte(unsigned char);
unsigned char receive_serial_byte(void);

That's it, we have a fully working Serial Interface that can send and receive. Everything we do from now will just be refinements or minor tweaks to suit the hardware, add functionality and most importantly learn a bit about RS232 Communications in a practical hands on manner.

Here is the code in full, if you press reset it will send you a greeting then enter Loop Back mode. Anything you type on the Terminal will now be echoed back in real time.  (make sure the terminal emulation software is not set for Echo too, or you will get each character twice).

The code is configured to use the Serial Adapter Board, if you are using an FTDI friend instead (or if you wish to try it out with the PICkit2 UART tool as a terminal)  then change the line:

#define SER_BIT         0                // Signal MODE

to read:

#define SER_BIT         1                // Signal MODE

Before you compile it:

Code: [Select]
//____________________________________________________
//
//   Soft UART Test for PIC16f690
//   Based on http://www.romanblack.com/bitbangserial.htm
//
//   C.A.Roper - 2013/02/27
//____________________________________________________
//
#include <xc.h>
#include <stdint.h>
//
// Configuration Fuses
//
#pragma config   FOSC   = INTRCIO   // Internal RC Osc 4Mhz I/O function on RA4 & RA5
#pragma config   MCLRE   = ON      // RA3 NOT avalable for input
#pragma config   CP      = OFF      // Code Protection Off
#pragma config   CPD   = OFF      // Data Protection Off
#pragma config   WDTE   = OFF      // Watchdog Timer Disabled
#pragma config   BOREN   = OFF      // Brownout Detection Disabled
#pragma config   PWRTE   = OFF      // Power-up Timer
//
// Prototypes of functions used
//
void send_serial_byte(unsigned char);
unsigned char receive_serial_byte(void);
//
//   Global Defines
//
#define SER_BAUD       50            // TMR1 (1Mhz/19200 baud) = 52
#define SER_BIT         0                // Signal MODE - 1 = Normal 0 = Inverted (Use Inverted for direct 232)
//
#define TxPin         PORTAbits.RA0   // which pin for serial out
#define RxPin         PORTAbits.RA1   // which pin for serial in
//
//   MAIN
//
void main(void)
{
//   Setup - Run once
   //
   //   Default I/O Setup
   //
   ANSEL            = 0;      // Disable Analog
   PORTA            = 0;      // Turn all Outputs OFF
   TRISA            = 0b001010;   // INPUTS: RA1 = RxPin, RA3 = Button
   T1CONbits.TMR1ON   = 1;      // Activate Timer 1
   TxPin            = SER_BIT;   // Set Tx Line to Idle State


   send_serial_byte('H');    // Send a Test String
   send_serial_byte('e');    // Send a Test String
   send_serial_byte('l');    // Send a Test String
   send_serial_byte('l');    // Send a Test String
   send_serial_byte('o');    // Send a Test String
   send_serial_byte(' ');    // Send a Test String
   send_serial_byte('W');    // Send a Test String
   send_serial_byte('o');    // Send a Test String
   send_serial_byte('r');    // Send a Test String
   send_serial_byte('l');    // Send a Test String
   send_serial_byte('d');    // Send a Test String
   send_serial_byte('!');    // Send a Test String
//
//   Main Loop - Run Forever
//
   while(1)
   {   
      if(RxPin == !SER_BIT)      // wait here for serial byte start bit
      {
         send_serial_byte(receive_serial_byte());   // Echo back     
      }   
   }
}
//
//   User Functions
//
void send_serial_byte(unsigned char data)
{
   unsigned char i = 8;         // 8 data bits to send
   
   TxPin = !SER_BIT;            // make start bit
   TMR1L = (256 - SER_BAUD);      // load TMR1 value for first baud;
   while(TMR1L & 1<<7);         // wait for baud
   
   while(i)                  // send 8 serial bits, LSB first
   {
      if(data & 1<<0) TxPin = SER_BIT;   // send data bit
      else          TxPin =!SER_BIT;
     
      data = (data >> 1);         // rotate right to get next bit
      i--;
      TMR1L -= SER_BAUD;         // load corrected baud value
      while(TMR1L & 1<<7);      // wait for baud
   }
   TxPin = SER_BIT;            // make stop bit
   
   TMR1L -= SER_BAUD;            // wait a couple of baud for safety
   while(TMR1L & 1<<7);
   TMR1L -= SER_BAUD;            // wait a couple of baud for safety
   while(TMR1L & 1<<7);
}
unsigned char receive_serial_byte(void)
{
   unsigned char RxChr;            // holds the serial byte that was received
   unsigned char i;               // Bit Index
   i = 8;                        // 8 data bits to receive
   
   TMR1L =(256 - (SER_BAUD - 19));      // load TMR1 value to offset ~center of RxBit
   while(TMR1L & 1<<7);               // wait for baud
   while(i)                     // receive 8 serial bits, LSB first
   {
      RxChr = (RxChr>>1);             // rotate right to store each bit
      if(  RxPin == 1)            // save data bit
          RxChr = RxChr | SER_BIT<<7;
      else RxChr = RxChr |!SER_BIT<<7;   
     
      i--;                     // Next Bit
      TMR1L -= SER_BAUD;            // load corrected baud value
      while(TMR1L & 1<<7);            // wait for baud
   }
   TMR1L -= SER_BAUD;               // wait for stop bit, ensure serial port is free
   while(TMR1L & 1<<7);
   return RxChr;
}




When we continue we will be looking at RS232 timing and how delicate it can be, till then


Cheers
Chris
« Last Edit: February 28, 2013, 12:11:59 pm by caroper »
 

Offline caroper

  • Regular Contributor
  • *
  • Posts: 193
  • Country: za
    • Take your PIC
Re: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s
« Reply #7 on: February 28, 2013, 04:29:12 pm »
In serial communications, just like comedy, timing is everything

RS232, or at least the subset that we are interested in, has no clock. In fact the acronym UART stands for Universal Asynchronous Receiver / Transmitter.  Because it is Asynchronous both sides of the link are expected to maintain accurate timing and conform strictly to the expected bit lengths. Most of the time (pun intended) we can ignore such esoteric issues when working with RS232, as both ends of the link generally have hardware to take care of it for us. In this case, however, we are providing the timing in software and we have to get it right. This issue of timing is not cut and dried either. One would expect that if the timing is wrong no data would be received, and that is generally true for major timing errors, but to make matters worse even a tiny error in timing, just a few uS (Microseconds)  will cause you the most headaches.

Unlike comedy, RS232 timing is no Joke. If the timing is out by only a few uS, 90% or more of your data will be perfect, but you will get an occasional glitch, a corrupt byte, that will show as the wrong character. Not really a problem when you are working with text, which is what Asynchronous Serial Communications and the ASCII character set were originally intended for,  but it is a major problem if the process you are controlling with your software is telling you that the temperature is 230 degrees and you receive it as 12 degrees.

Large portions of the RS232 Standard are dedicated to ways of getting around this. That is why you have things like Parity Bits and the options for hardware and/or software handshaking. The original RS232 Specification calls for a DB25 Connector and most of the pins were used. As hardware became faster and more stable a lot of them were no longer used that often and so the DB9 connector took over. Most of us, however, have only ever been exposed to Rx and Tx lines, a two wire (plus ground) implementation of RS232. That is because modern equipment has become so fast and its timing so accurate, combined with the communications software (anyone here remember Kermit or XModem?) that we use and the relatively short signal paths (RS232 Full Standard can go upto a Kilometer or more) have made most of the RS232 Standard obsolete.

If you have been trying out the code presented here you will probably have received the occasional corrupt character already (I hope it was not so bad that you just gave up, this post is going to provide the reason and the solution).  Infact if you look at the code where the timing is defined you will see:

#define SER_BAUD       50            // TMR1 (1Mhz/19200 baud) = 52

Notice how the comment says it should be 52 and I have used 50,  that is not a typo, that is the result of tweaking the timing to suit my chip.

Lets do a bit of Math, I am running with a 4Mhz system clock and Timer 1 is counting the Instruction Clock, so divide by 4 and we have a 1Mhz Clock feeding a 16Bit timer. We are only using TMR1L, so effectively we are using Timer 1 as an 8 Bit Timer (a waste I know and we will address that later when we fully understand the timing issues)  so we are counting 1uS pulses.

That means that the difference between 50 (which works well with my Chip) and 52 which the calculation says it should be is only 2uS.  Yet it makes the difference between reliable data communications and illegible junk as the screenshots below will show.



That is the result of setting SER_BAUD to 52, if anyone tried my code and got that result I don’t blame them for giving it up as a bad joke.  lets try it with 51:



That is much better, it is the same data as before but with a one Microsecond difference in timing. Notice though that it is not perfect, the second L in the third string has corrupted. Probably good enough for text communications or if we applied an error correcting algorithm, but we want to use this for debug values or reading sensors, so we have to be able to trust our results.



With a value of 50 we are finally getting acceptable results and I have used the the Capture Tab in RealTerm to run samples for well over an Hour with no errors.

So what is happening here?

This is where the Logic Tools on your PICkit2 becomes invaluable, so much so that it is worth buying one even if, like me, you also have a PICkit3 and an ICD. The more timing issues you address,  the more you will appreciate the PICKit2 Logic Tool.

And lets face it if you develop any kind of control software, even just driving a hobby servo, timing can become critical.

When Roman developed his Software he was using an 8Mhz crystal oscillator with a PIC18 device on a MikroElektronica development Kit.

We are trying to debug it on a PIC16 with a 4Mhz RC Clock on a Low Pin Count Demo board, there are bound to be differences in tolerances.

I have found a very useful table that shows the expected timing for RS232 wave-forms at various common BAUD Rates, so lets see how well our implementation compares.



We can make the assumption that our PC with its Quad Core processor running at 2.4 Ghz is at least capable of generating an accurate timing signal, so lets start by measuring a waveform captured from the PC to use as a baseline.

Attach the RS232 Adapter Board  (or FTDI if that is what you are using) to the Tx and Rx pins (RA0 and RA1 - don't  forget the Gnd connection too), then Load up the PICkit2 Logic Tool and set the sample rate to:  500 kHz - 2 ms Window (note that we get a warning about signals greater than 250Khz, but as we are only expecting about a 20Khz signal we have nothing to worry about) then Send a U from RealTerm.



We want to use our cursors to measure the Frame Width (1 start bit, 8 data bits) and a Bit Width.


Zoom in to 2x and place your cursors as accurately as possible to see what you get.

I measured 468uS for the Frame and 52uS for a bit. The table says it should be 468.75uS and 52.08uS.  But we are not going to quibble over nanoSeconds, we could never place the cursors accurately enough.

You should have captured two waveform's, the one on Ch2 is the Frame we received from the PC, the one that follows it on Ch1 is the Frame we sent back. Let’s measure that one too and compare the timing.



I measure 476 uS and 52 uS so our bit width is exact but our frame length is a little large. That is because of the Start Bit which in my case I measure as being 72 uS. Having the Start bit stretched is not normally a problem, infact stretching the start bit can sometimes be desirable as it gives the receiver time to notice it and begin reading the frame, it is the data bits that need the accuracy.

If you look at out code the start bit is is followed by:


Code: [Select]
   while(i)                  // send 8 serial bits, LSB first
   {
      if(data & 1<<0) TxPin = SER_BIT;   // send data bit
      else          TxPin =!SER_BIT;

That while(i) construct will take a few cycles to execute before we send the first data bit. If we forked out US$500 for the Optimizer we may be able to reduce it, but as it will not really effect our data, why bother.Don’t be tempted to try and save a line of code by using:

while(i--)                  // send 8 serial bits, LSB first

and eliminating the i--; that follows the data send, that will stretch the Start Bit too much and totally ruin your day, I know, I found out the hard way.now lets repeat the measurements with the calculated value of:
#define SER_BAUD       52            // TMR1 (1Mhz/19200 baud) = 52




The waveforms look fine but RealTerm is showing Junk echoed back.  How could that be? If you zoom in to x4 and measure the bit width of all the bits you will notice that they are getting progressively smaller, ranging from 64uS which is way above the 52 uS we expect to 48 uS which is below. Without the ability to take period measurements we could have easily missed that. The result of course raises the question of how the periods can drift when the the timer value is constant.

To find the answer to that we are going to have to look at the timing inside the PIC rather than just the Rx and Tx lines.
I bet you are beginning to realise that porting a Software UART involves a lot more than just copying someone else's  code.

But that will have to wait, my supper is on the table.

Cheers,
Chris
« Last Edit: February 28, 2013, 08:12:54 pm by caroper »
 

Offline caroper

  • Regular Contributor
  • *
  • Posts: 193
  • Country: za
    • Take your PIC
Re: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s
« Reply #8 on: March 02, 2013, 08:19:12 pm »
Advanced Debugging with the PICkit2 Logic Tools




So far we have only used two if the available inputs on our Logic Analyzer,  in order to get a better look at what is actually happening inside the PIC in relation to the signals we have been reading on the RS232 Port we will press the third Line into service.


On the Small Pin Count Demo Board, that I have been using for this project, the ICSP header has the AUX pin connected to RA4, so I will use that as the Debugging Output. The following is added to our Pin Definitions.



#define dbOut          PORTAbits.RA4   // Debugging Output 


Nothing needs to change in our setup routine as we have already declared all unused pins to be outputs by default.


We can use this output for several things, much like we would if we had an LED attached, but because it is going to our Logic Analyzer it allows us to use it the create Trigger signals and timing pulses to help us better analyze our data.


In the previous section we were looking at the Rx pin and the Tx Pin and the timing involved, but that gives us no real idea of the timing inside the PIC. It would help us better understand the inner workings if we could see how the PIC is responding to changes on the RX line and the dbOut pin is perfect for that. We will use it to analyze the receive_serial_byte() function.


Before we do that, however, as a result of our earlier tests we had discovered that our Start Bit was very stretched, I decided to address that issue before moving on to the receive_serial_byte() function as the send_serial_byte() function is arguably the most useful.


Unfortunately that opened up a can of worms that made me seriously rethink the usefulness of XC8 as well as highlighting another major pit fall in Porting C code between vendors, How Good is the Generated Assembler Code.


Roaman developed his code in MicoC, a nice mature compiler with good optimization.


We are porting it to a Free version of XC8, which in addition to not having advanced Optimization enabled in Free mode, it would appear is deliberately crippling the generated code. If that were the case I would consider it to be equivalent to extortion. But searching the MicroChip forums it would appear that it is not so much crippled as not optimized in the slightest. Not even the obvious optimization that you would expect any compiler to do.


The bottom line is that rather than having the MBA's from Marketing fired, Dave's Rant on the PICkit3 got them all transfected out of the Engineering Department and straight into the Software Tools division. I, like many others who had used XC32 - which has good optimization even in free mode,  that XC8 would be  based on GCC and that the same base code would cover both. I could not have been more wrong.
XC8 is based on HTC - Microchip Bought HiTech a couple of years ago. and XC8 Free mode is the crippled HTC that now supports all chip rather than a select few, but with all optimizations removed.


After several hours of playing with execution order and alternative logic forms, all whilst monitoring code size and Timeing waveforms, this is the best I could come up with in terms of hand optimizing the function:


Code: [Select]

void send_serial_byte(unsigned char data)
{
unsigned char index = 8; // 8 data bits to send

TMR1L = (256 - SER_BAUD); // load TMR1 value for first baud;
TxPin = !SER_BIT; // make start bit
while(TMR1L > 127); // wait for baud

do // send 8 serial bits, LSB first
{
if(data & 1<<0)
TxPin = SER_BIT;// send data bit
else TxPin =!SER_BIT;

TMR1L -= SER_BAUD; // load corrected baud value
index--;
data = (data >> 1);          // rotate right to get next bit
while(TMR1L > 127); // wait for baud
}
while(index);

TxPin = SER_BIT; // make stop bit

TMR1L -= SER_BAUD; // wait for baud
while(TMR1L > 127);
}




The While Loop becomes a Do loop and the references to  (TMR1L & 7<<1) become (TMR1L > 127), why the last change makes a difference is beyond me, it should be the same thing to the compiler.  References to local variable i become index, a cosmetic change as that is more readable and indicative of its use and finally the Start Bit, the Data Shift Function and the index-- have been moved into the timing loops, after the Timer is reseeded, effectively making them free, as far as execution cycles are concerned, as we are waiting for the timeout at that point.


The Start bit is thus reduced to only 64 uS and the average bit time is now 51.5 uS which should be within the tolerance of most receiving devices.  I still have no answer as to why the bit lengths vary when they are generated in a tight loop but I suspect it is the code generated by the if Statement that is at fault here. Further optimization does not seam possible as, contrary to expectation the statement:


TxPin = (data & 1) ? SER_BIT : !SER_BIT;


Actually generates more code and takes longer to execute. That is one of the reasons I began to suspect that the generated code is deliberately  deoptimized to encourage purchase of the Licensed version. David's MBA's at work again.


TBC....






 








 














Offline caroper

  • Regular Contributor
  • *
  • Posts: 193
  • Country: za
    • Take your PIC
Re: Tutorial - Advanced Debugging and Code Porting with Mid Range PIC’s
« Reply #9 on: March 03, 2013, 09:41:34 am »



First let's look at inserting our debug code.


What we are interested in is what the software is seeing  in relation to what is being sent.
Currently our PICkit2 Logic Analyzer is showing us the RxPin and TxPin from the perspective of the terminal device, we are going to use the third trace to show the Software's view of the RxPin.


we already defined dbOut so e can just go ahead and use it, however, as we are primarily interested in timing relationships here we need to be mindful of how manipulating the dbOut Pin will impact on our results.  WARNING: UGLY Code Practices ahead !!!!


In order to raed the RxPin value as close as we can to what the Software sees I will be assigning RxPin to dbOut at the same time as it is read by the software by the software for the conditional tests, I have also broken down the compound statement we had in Main into separate Read and Write Functions with a local variable char MyChar as the intermediary.


Our main loop becomes:


Code: [Select]

while(1)
{   
if((dbOut = RxPin) == !SER_BIT)      // wait here for serial byte start bit
{
char MyChar = receive_serial_byte();   
send_serial_byte(MyChar); // Echo back     
}   
}


That will allow us to see how long it takes our code to detect the start bit.


Within the receive_serial_byte() function we will use the same trick when we test the RxPin:


Code: [Select]

if((dbOut = RxPin) == 1)    // save data bit
RxChr |=  SER_BIT<<7;
else RxChr |= !SER_BIT<<7;   


I also renamed i to index and moved the decrement into the timing loop as we did with the receive_serial_byte() function.


The full program now looks like this:
Code: [Select]

//____________________________________________________
//
//   Soft UART Test for PIC16f690
//   Based on http://www.romanblack.com/bitbangserial.htm
//
// Modified for portability over Baseline and Midrange PICs
// and ported to the xc8 compiler
//
// Supports Inverted or Noninverted Serial(RS232) I/O on user selected pins
//
//   C.A.Roper - 2013/03/03
//____________________________________________________
//
#include <xc.h>
#include <stdint.h>
//
// Configuration Fuses
//
#pragma config   FOSC = INTRCIO   // Internal RC Osc 4Mhz I/O function on RA4 & RA5
#pragma config   MCLRE = ON // RA3 NOT avalable for input
#pragma config   CP = OFF // Code Protection Off
#pragma config   CPD = OFF // Data Protection Off
#pragma config   WDTE = OFF // Watchdog Timer Disabled
#pragma config   BOREN = OFF // Brownout Detection Disabled
#pragma config   PWRTE = OFF // Power-up Timer
//
// Prototypes of functions used
//
void send_serial_byte(unsigned char);
unsigned char receive_serial_byte(void);
void Send_Hello(uint16_t);
//
//   Global Defines
//
#define SER_BAUD 50 // TMR1 (1Mhz/19200 baud) = 52
#define SER_BIT 0 // Signal MODE - 1 = Normal 0 = Inverted (Use Inverted for direct 232)
//
#define TxPin PORTAbits.RA0 // which pin for serial out
#define RxPin PORTAbits.RA1 // which pin for serial in
#define dbOut PORTAbits.RA4 // which pin for serial in
//
//   MAIN
//
void main(void)
{
//
//   Setup - Run once
//
//   Default I/O Setup
//
ANSEL            = 0; // Disable Analog
PORTA            = 0; // Turn all Outputs OFF
TRISA            = 0b001010; // INPUTS: RA1 = RxPin, RA3 = Button
T1CONbits.TMR1ON = 1; // Activate Timer 1
TxPin            = SER_BIT; // Set Tx Line to Idle State


//Send_Hello(10000);
//
//   Main Loop - Run Forever
//
while(1)
{   
if((dbOut = RxPin) == !SER_BIT)      // wait here for serial byte start bit
{
char MyChar = receive_serial_byte();   
send_serial_byte(MyChar); // Echo back     
}   
}
}
//
//   User Functions
//
void send_serial_byte(unsigned char data)
{
unsigned char index = 8; // 8 data bits to send

TMR1L = (256 - SER_BAUD); // load TMR1 value for first baud;
TxPin = !SER_BIT; // make start bit
while(TMR1L > 127); // wait for baud

do // send 8 serial bits, LSB first
{
if(data & 1<<0)
TxPin = SER_BIT; // send data bit
else TxPin =!SER_BIT;

TMR1L -= SER_BAUD; // load corrected baud value
index--;
data = (data >> 1);         // rotate right to get next bit
while(TMR1L > 127); // wait for baud
}
while(index);

TxPin = SER_BIT; // make stop bit

TMR1L -= SER_BAUD; // wait for baud
while(TMR1L > 127);
}


unsigned char receive_serial_byte(void)
{
unsigned char RxChr = 0; // holds the received byte
unsigned char index = 8; // 8 data bits to receive

TMR1L =(256 - (SER_BAUD/2)); // load TMR1 value & offset received ~=center of RxBit
while(TMR1L & 1<<7);            // wait for baud

while(index) // receive 8 serial bits, LSB first
{
RxChr = (RxChr>>1);         // rotate right to store each bit

if((dbOut = RxPin) == 1)    // save data bit
RxChr |=  SER_BIT<<7;
else RxChr |= !SER_BIT<<7;   

TMR1L -= SER_BAUD ;       // load corrected baud value
index--;
while(TMR1L & 1<<7);        // wait for baud
}

TMR1L -= SER_BAUD;              // wait for stop bit, ensure serial port is free
dbOut = RxPin;
while(TMR1L & 1<<7);

return RxChr; // return received byte
}


Set the Logic Analyzer to trigger on the rising edge of Ch 3 (Don't forget to set the others to Don't Care) Run it, send a 'U' and you should get the following result:







it only took 16uS to detect the Start Bit so that is OK.


The line:



   TMR1L =(256 - (SER_BAUD/2));   // load TMR1 value & offset received ~=center of RxBit


should set the PIC to sample each successive bit in the center, this is the critical part of RS232 Timing and what allows us a slight timing difference between each end. By sampling in the center  of the bit we don't care if the bit is slightly stretched or compressed, so we need to be as close to the center as we can be.


In our case we are sampling too late. Rather than adjust the timing  value which being BAUD/2 should be spot on I decided to try the same optimization as I used in the send_serial_byte() function and change the while loop into a do loop:


Code: [Select]

unsigned char receive_serial_byte(void)
{
unsigned char RxChr = 0; // holds the received byte
unsigned char index = 8; // 8 data bits to receive

TMR1L =(256 - (SER_BAUD/2)); // load TMR1 value & offset received ~=center of RxBit
while(TMR1L & 1<<7);            // wait for baud

do // receive 8 serial bits, LSB first
{
RxChr = (RxChr>>1);         // rotate right to store each bit

if((dbOut = RxPin) == 1)    // save data bit
RxChr |=  SER_BIT<<7;
else RxChr |= !SER_BIT<<7;   

TMR1L -= SER_BAUD ;       // load corrected baud value
while(TMR1L & 1<<7);        // wait for baud
}
while(--index);

TMR1L -= SER_BAUD;              // wait for stop bit, ensure serial port is free
dbOut = RxPin;
while(TMR1L & 1<<7);

return RxChr; // return received byte
}


The result is:







A definite improvement, our first sample is at 26uS  which is within a microsecond of the center of the RxPin and the last sample is at 27us 7 samples later. I doubt we could get it better than that on an RC oscillator.

















Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf