Software UART PIC10F322

(instructions, reset, WDT, specifications...) PIC10F2xx, PIC12F5xx, PIC16F5x

Software UART PIC10F322

Postby viki2000 » Fri Apr 29, 2016 9:20 am

I watched next example:
http://saeedsolutions.blogspot.de/2012/ ... nging.html
And I tested it myself. It is working.
To be able to post all the code here I combined all the nice spread subroutines and headers in a single C file below:
Code: Select all
#include <xc.h>

#ifndef _XTAL_FREQ
    #define _XTAL_FREQ 4000000
#endif

#define Baudrate 1200 //bps
#define OneBitDelay (1000000/Baudrate)
#define DataBitCount 8 // no parity, no flow control
#define UART_RX GP1 // UART RX pin
#define UART_TX GP0 // UART TX pin
#define UART_RX_DIR TRISIO1 // UART RX pin direction register
#define UART_TX_DIR TRISIO0 // UART TX pin direction register

//Function Declarations
void InitSoftUART(void);
unsigned char UART_Receive(void);
void UART_Transmit(const char);

__CONFIG(FOSC_INTRCIO & WDTE_OFF & PWRTE_ON & MCLRE_OFF & BOREN_ON & CP_OFF & CPD_OFF);

void main()
{
 unsigned char ch = 0;

 ANSEL = 0x00; // Set ports as digital I/O, not analog input
 ADCON0 = 0x00; // Shut off the A/D Converter
 CMCON = 0x07; // Shut off the Comparator
 VRCON = 0x00; // Shut off the Voltage Reference
 //TRISIO = 0x08; // GP3 input, rest all output
 GPIO = 0x00; // Make all pins 0
 
 InitSoftUART(); // Intialize Soft UART

 while(1)
 {
  ch = UART_Receive(); // Receive a character from UART
  UART_Transmit(ch); // Echo back that character
 }
}


void InitSoftUART(void) // Initialize UART pins to proper values
{
 UART_TX = 1; // TX pin is high in idle state
 
 UART_RX_DIR = 1; // Input
 UART_TX_DIR = 0; // Output
}

unsigned char UART_Receive(void)
{
 // Pin Configurations
    // GP1 is UART RX Pin

 unsigned char DataValue = 0;

 //wait for start bit
 while(UART_RX==1);

 __delay_us(OneBitDelay);
 __delay_us(OneBitDelay/2); // Take sample value in the mid of bit duration

 for ( unsigned char i = 0; i < DataBitCount; i++ )
 {
  if ( UART_RX == 1 ) //if received bit is high
  {
   DataValue += (1<<i);
  }

  __delay_us(OneBitDelay);
 }

 // Check for stop bit
 if ( UART_RX == 1 ) //Stop bit should be high
 {
  __delay_us(OneBitDelay/2);
  return DataValue;
 }
 else //some error occurred !
 {
  __delay_us(OneBitDelay/2);
  return 0x000;
 }
}

void UART_Transmit(const char DataValue)
{
 /* Basic Logic
   
    TX pin is usually high. A high to low bit is the starting bit and
    a low to high bit is the ending bit. No parity bit. No flow control.
    BitCount is the number of bits to transmit. Data is transmitted LSB first.

 */

 // Send Start Bit
 UART_TX = 0;
 __delay_us(OneBitDelay);

 for ( unsigned char i = 0; i < DataBitCount; i++ )
 {
  //Set Data pin according to the DataValue
  if( ((DataValue>>i)&0x1) == 0x1 ) //if Bit is high
  {
   UART_TX = 1;
  }
  else //if Bit is low
  {
   UART_TX = 0;
  }

     __delay_us(OneBitDelay);
 }

 //Send Stop Bit
 UART_TX = 1;
 __delay_us(OneBitDelay);
}

Then I decided to try it on PIC10F322. The code was modified with differenr name for pins and regfisters as below:
Code: Select all
#include <xc.h>

#ifndef _XTAL_FREQ
    #define _XTAL_FREQ 4000000
#endif

#define Baudrate 1200 //bps
#define OneBitDelay (1000000/Baudrate)
#define DataBitCount 8 // no parity, no flow control
#define UART_RX RA1 // UART RX pin
#define UART_TX RA0 // UART TX pin
#define UART_RX_DIR TRISA1 // UART RX pin direction register
#define UART_TX_DIR TRISA0 // UART TX pin direction register

//Function Declarations
void InitSoftUART(void);
unsigned char UART_Receive(void);
void UART_Transmit(const char);

__CONFIG(FOSC_INTOSC & MCLRE_OFF & WDTE_OFF & LVP_OFF & CP_OFF &
         WRT_OFF & PWRTE_OFF & WRT_OFF & BOREN_ON & LPBOR_ON & BORV_LO);

void main()
{
 unsigned char ch = 0;

 ANSELA = 0x00; // Set ports as digital I/O, not analog input
 ADCON = 0x00; // Shut off the A/D Converter
 FVRCON = 0x00; // Shut off the Voltage Reference
 PORTA = 0x00; // Make all pins 0
 
 InitSoftUART(); // Intialize Soft UART
 
 InitSoftUART(); // Intialize Soft UART

 while(1)
 {
  ch = UART_Receive(); // Receive a character from UART
  UART_Transmit(ch); // Echo back that character
 }
}


void InitSoftUART(void) // Initialize UART pins to proper values
{
 UART_TX = 1; // TX pin is high in idle state
 
 UART_RX_DIR = 1; // Input
 UART_TX_DIR = 0; // Output
}

unsigned char UART_Receive(void)
{
 // Pin Configurations
    // GP1 is UART RX Pin

 unsigned char DataValue = 0;

 //wait for start bit
 while(UART_RX==1);

 __delay_us(OneBitDelay);
 __delay_us(OneBitDelay/2); // Take sample value in the mid of bit duration

 for ( unsigned char i = 0; i < DataBitCount; i++ )
 {
  if ( UART_RX == 1 ) //if received bit is high
  {
   DataValue += (1<<i);
  }

  __delay_us(OneBitDelay);
 }

 // Check for stop bit
 if ( UART_RX == 1 ) //Stop bit should be high
 {
  __delay_us(OneBitDelay/2);
  return DataValue;
 }
 else //some error occurred !
 {
  __delay_us(OneBitDelay/2);
  return 0x000;
 }
}

void UART_Transmit(const char DataValue)
{
 /* Basic Logic
   
    TX pin is usually high. A high to low bit is the starting bit and
    a low to high bit is the ending bit. No parity bit. No flow control.
    BitCount is the number of bits to transmit. Data is transmitted LSB first.

 */

 // Send Start Bit
 UART_TX = 0;
 __delay_us(OneBitDelay);

 for ( unsigned char i = 0; i < DataBitCount; i++ )
 {
  //Set Data pin according to the DataValue
  if( ((DataValue>>i)&0x1) == 0x1 ) //if Bit is high
  {
   UART_TX = 1;
  }
  else //if Bit is low
  {
   UART_TX = 0;
  }

     __delay_us(OneBitDelay);
 }

 //Send Stop Bit
 UART_TX = 1;
 __delay_us(OneBitDelay);
}


In case the code os too long and cannot be seen well above, then I uploaded it one more time here as txt:
UART 12F675:
https://drive.google.com/open?id=0BwXmK ... kZfS0dnQ2M

UART 10F322:
https://drive.google.com/open?id=0BwXmK ... S04c1BGR1U

Unfortunately is not working with Pic10F322. It sends out only one long “0” as a series of 8bits all 0 logic.
Any help to debug and see what is wrong with it is appreciated.
viki2000
 
Posts: 5
Joined: Fri Apr 29, 2016 9:17 am
PIC experience: Experienced Hobbyist

Re: Software UART PIC10F322

Postby viki2000 » Mon May 02, 2016 10:38 am

The code works fine as it is from beginning, also for 10F322.
The issue was related with Proteus settings, in fact the Virtual Terminal. I did not edit the simple settings of baud rate of virtual terminal to match the settings of the UART software. Default in Proteus the baud rate is 9600 and set in UART software was 1200, from here the difference in timing measured with virtual oscilloscope.
Here are the Virtual Terminal settings in Proteus:
http://i926.photobucket.com/albums/ad10 ... eddlsw.jpg
http://i926.photobucket.com/albums/ad10 ... ypm6sk.jpg

Today I have tried with a real PIC10F322 and works as follows.
I used the AC103011 development board for PIC10F322 as described here (http://www.microchip.com/forums/m673119.aspx#880627 ), which was around 10€.
And for communication with PC, I used a cheap 2€ USB To RS232 TTL UART cable based on PL2303HX from EBay, similar with this one:
http://www.ebay.com/itm/New-USB-To-RS23 ... 1990260481
http://www.ebay.com/itm/USB-RS232-TTL-U ... 2a56f0e041
I like this USB-RS232 better than MAX232 with a lot of capacitors around, because is compact and you do not need so many wires, components and breadboards. It provides also 5V from USB for the AC103011 PIC10F322 board. PICKit3 can be also set to program the AC103011 board without external power supply, so PICKit 3 can provide the 5V. Then everything is with less wires around.
Instead of old HyperTerminal, I used RealTerm Serial terminal:
http://realterm.sourceforge.net/
With the internal clock set to 4MHz, the 10F322 works fine with baud rate of max. 1200. When is 2400 or higher, then the C code and internal clock is too slow and on RealTerm I see garbage, wrong characters.
If I increase the frequency of the internal clock to 8MHz, then the baud rate can be increased to 2400. Again when is 4800 or higher is not good.
At 16MHz internal clock for 10F322 we can use baud rate to 4800, but not higher, at least not with this code.
viki2000
 
Posts: 5
Joined: Fri Apr 29, 2016 9:17 am
PIC experience: Experienced Hobbyist

Re: Software UART PIC10F322

Postby viki2000 » Wed May 04, 2016 2:48 pm

Based on http://datadogsystems.com/Datadog/Folde ... t/uart.pdf , here is full working code in ASM with 10F322. It take 5% RAM and 12% FLASH.
Code: Select all
    list        p=10F322           
    #include    <p10F322.inc>   

;***** CONFIGURATION
    __CONFIG    _WRT_OFF & _BORV_24 & _LPBOR_OFF & _LVP_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_OFF & _WDTE_OFF & _BOREN_OFF & _FOSC_INTOSC

   
;***** VARIABLE DEFINITIONS
        UDATA
SERBUF    res 1
TEMP    res 1
COUNT    res 1
   
;*****************************************************************
    org     0x000               ; Set the reset vector
    goto    start               ; Go to the beginning of main
;*******************Macro definitions*****************************

    ; ********************************************************************
    ; SERIAL IN CHANNEL ROUTINE
    ; ********************************************************************
inch_n
    btfsc PORTA,0 ; SKIP ON START BIT = 1 (A "MARK")
    goto inch_n ; ELSE KEEP LOOKING FOR A START BIT
    movlw 8 ; START SERIAL INPUT SEQUENCE
    movwf TEMP ; COLLECT 8 DATA BITS
    clrf SERBUF ; CLEAR SERIAL CHARACTER BUFFER
    call half_baud ; DELAY FOR ONE HALF BAUD TIME
    btfsc PORTA,0 ; FALL THRU IF START BIT STILL = 1 (A "MARK")
    goto inch_n ; ELSE IT WAS JUST A NOISE SPIKE, KEEP LOOKING
inch_n1
    call baud ; DELAY ONE BAUD-BIT TIME ( = 1/BAUD-RATE)
    bcf STATUS,0 ; CLEAR THE CARRY BIT
    rrf SERBUF,F ; ROTATE CRY -> MSB, ROTATE MSB RIGHT
    btfsc PORTA,0 ; IS IT A "MARK" ?
    bsf SERBUF,7 ; ...SKIP IF YES, ELSE SET BIT TO LOGIC '1'
    decfsz TEMP,F ; EIGHT COUNTS YET?
    goto inch_n1 ; ...NO, GET ANOTHER BIT
    call baud ; DELAY FOR THE STOP BIT
    movf SERBUF,W ; PUT THE RECEIVED CHARACTER IN REG 'W'
    ;retlw 0 ; NOTE: REM THIS OUT IF YOU NEED AN "ECHO"
         ; ...AND FALL THROUGH TO THE 'OUTCH' ROUTINE

   
    ; ********************************************************************
    ; SERIAL OUT CHANNEL ROUTINE
    ; ********************************************************************
outch_n ; THIS ROUTINE USES 8 DATA BITS
    movwf SERBUF ; SERBUF CONTAINS CHARACTER TO XMT
    movlw 8 ; THE CHARACTER HAS 8 BITS
    movwf TEMP
    bcf LATA,1 ; SET START-BIT TO A "SPACE"
    call baud ; WAIT ONE BAUD TIME
outch_n1
    rrf SERBUF,F ; ROTATE THE FIRST BIT INTO CARRY
    btfss STATUS,0 ; TEST THE CARRY BIT
    bcf LATA,1 ; IF BIT IS 0 SET OUTPUT PIN TO A "0" (SPACE)
    btfsc STATUS,0 ; TEST THE CARRY BIT AGAIN
    bsf LATA,1 ; IF BIT IS 1 SET OUTPUT PIN TO A "1" (MARK)
    call baud ; ONE BAUD-BIT DELAY
    decfsz TEMP,F ; IF COUNT IS ZERO THEN XMIT A STOP BIT
    goto outch_n1 ; ...ELSE XMIT NEXT BIT
    rrf SERBUF,F ; ROTATE CARRY, GET THE MSB BACK INTO BIT 7
    bsf LATA,1 ; SET PIN TO A 1 (A "MARK") FOR THE STOP BIT
    call baud ; FIRST BAUD-BIT DELAY
    call baud ; SECOND BAUD-BIT DELAY
    retlw 0 ; RETURN WITH THE CHARACTER IN SERBUF
   
       
    ; ********************************************************************
    ; BAUD ROUTINE @ 4 MHz
    ; BAUD RATE: CONSTANT:
    ; 1200 Baud D'137'
    ; 2400 Baud D'68'
    ; 4800 Baud D'34'
    ; 9600 Baud D'16'
    ; 19200 Baud D'8'
    ; 38400 Baud and up - use 'NOP' delays
    ; VARIABLES USED: REG 'COUNT'
    ; ROUTINES CALLED: NONE
    ; ********************************************************************
baud:    ; AT 2400 BAUD THE PERIOD IS 416.6 US
    ; CLK = 4MHz
    movlw D'68' ; 1 US (BAUD RATE CONSTANT)
    movwf COUNT ; 1 US
baud1:
    decfsz COUNT,F ; 1 US (+ 1 US MORE IF SKIP)
    goto baud1 ; 2 US
    ; FALL THRU...AFTER 1+1+3x68+1 = 207 US
half_baud:
    movlw D'68' ; 1 US
    movwf COUNT ; 1 US
hbaud1:
    decfsz COUNT,F ; 1 US (+ 1 US MORE IF SKIP)
    goto hbaud1 ; 2 US
    retlw 0 ; ...AFTER 1+1+3x68+1 = 207 US (X2=414 US)         

   
;***** MAIN PROGRAM *****************************************************

;***** Initialisation
start   
        banksel OSCCON
    movlw   b'01010000'     ; Int. osc. 4 MHz
    movwf   OSCCON
; Set some SFR to zero
   
        clrf    ANSELA      ; Make all GPIOs digital I/O
        clrf    WPUA        ;
        clrf    IOCAN       ;
        clrf    IOCAP       ;
        clrf    IOCAF       ;
    clrf    OPTION_REG  ;
        clrf    PIE1        ; Disable all peripheral interrupt sources
   
        bcf     TRISA,1     ; Make RA1(TXD) an output
        bsf     WPUA,0      ; Enable pull up on RXD (RA0)
    bsf     TRISA,0     ; Make RA0(RXD) an input
    bsf     LATA,1        ; Set RA1 as "1" logic

;***** Main loop
main_loop

    call    inch_n
    goto    main_loop       ; repeat forever

    END


- With internal clock set to 4MHz, the serial echo works fine up to 9600 baud rate on simulation Proteus.
- Starting with 19200 baud rate we receive errors, wrong characters. It is the same as with PIC10F200.
- I think for 19200 and higher baud rates we must increase the internal clock to 8 or 16MHz, but then the code used for baud rate subroutines must have new constants. Perhaps 19200 and 38400 may still work with 4MHz as DataDogs said, but I think the baud rate subroutines must have new constants anyway. At least what was suggested does not work for me, not in Proteus, but up to 9600 baud rate everything is fine.
viki2000
 
Posts: 5
Joined: Fri Apr 29, 2016 9:17 am
PIC experience: Experienced Hobbyist

Re: Software UART PIC10F322

Postby viki2000 » Wed May 04, 2016 2:49 pm

Finally I made some practical tests with a real PIC10F322 and a PC using USB-RS232 TLL cable running RealTerm terminal.
The ASM code above with 10F322 works fine with 4MHz internal clock up to 9200 baud rate. Starting with 19200 baud rate we get garbage character, indicating the necessity to change the constants in baud subroutine. Of course here comes the problem already mentioned by Ian.M, we have tolerances for each PIC oscillator frequency and I am afraid we cannot rely on high baud rates. I do nopt know how the guys from DataDogs arrived to 38400 baud rate with that code. Maybe was a case for a specific PIC.
Then I remembered that there is one more C compiler, the CCS C compiler, which luckily has built in software RS232 subroutines.
It would be very nice to have similar subroutines inside the XC8 C compiler.
Then I made some tests with CCS C compiler. The C code is very short:
Code: Select all
#include <10F322.h>
#device ADC=16
#use delay(internal=4000000)
#use rs232(baud=19200, xmit=PIN_A1, rcv=PIN_A0)
unsigned char ch;
 
void main()
{
   while(TRUE)
   {
      ch = getc();
      putc(ch);
   }
}

The code uses 9%RAM and 17% Flash.
I tested the code with real 10F322 for different internal frequencies and different baud rates and works as follows:
1) Internal clock 4MHz - the baud rate OK up to 19200. With 38400 no chance.
2) Internal clock 8MHz - the baud rate OK up to 115200. With 230400 no chance.
3) Internal clock 16MHz - the baud rate OK also 230400.
4) Higher than 230400 is not accepted by CCS C compiler.
I think 115200 is quite decent speed for RS232.
That shows that even the above ASM code from DataDogs will work definitely for higher baud rates when internal clock is changed and the constants from baud subroutine to be adapted for other delays corresponding to those baud rates. I think that is another work to find them.
viki2000
 
Posts: 5
Joined: Fri Apr 29, 2016 9:17 am
PIC experience: Experienced Hobbyist


Return to 12-Bit Core

Who is online

Users browsing this forum: No registered users and 5 guests

cron