Problem with Soft UART for attiny45

Discussion in 'Embedded Systems and Microcontrollers' started by allahjane, Feb 21, 2013.

  1. allahjane

    Thread Starter Member

    Sep 19, 2012
    75
    1
    I've been trying hard on this. I have two avr AT-Tiny45 ,these lack hardware Uart so I'm designing my own using Timers and their interrupt to switch a GPIO pin to H/L state as required by the transmission format and protocol.

    So far I have made these two test programs, Using async mode with 1 stop bit and no parity

    Transmitter code
    Code ( (Unknown Language)):
    1.  
    2. /*
    3.  * tiny45Tx.c
    4.  *
    5.  
    6.  */
    7. #define F_CPU 1000000
    8. #include <avr/io.h>
    9. #include <avr/interrupt.h>
    10. #include <util/delay.h>
    11. unsigned short byteToSend,bit;
    12. unsigned int timerCount=0;
    13.  
    14. short state=0,idle=0x00,transmitting=0xff,bitCount=0;
    15.  
    16.  
    17. void setTimer(int on){
    18. if(on){
    19. TCNT0=0;
    20. TIMSK|=(1<<OCIE0A);
    21. TCCR0A|=(1<<WGM01);
    22. OCR0A=25;
    23. TCCR0B|=(1<<CS01)|(1<<CS00);
    24. }
    25. else{
    26. TIMSK&=~(1<<OCIE0A);
    27. TCCR0B&=~((1<<CS01)|(1<<CS00));
    28. }
    29.                 }
    30.  
    31.  
    32. void setupIO(){
    33. DDRB=(1<<PB4)|(1<<PB0);
    34. PORTB|=(1<<PB3);
    35. state=idle;
    36. PORTB|=(1<<PB4)|(1<<PB0);
    37. _delay_ms(500);
    38. PORTB&=~((1<<PB4)|(1<<PB0));
    39. _delay_ms(500);
    40.  
    41. }
    42.  
    43. void sendByte(unsigned short dataByte)
    44. {
    45. if(state!=idle)
    46. return;
    47. else
    48. {
    49. bitCount=-1;
    50. state=transmitting;
    51. byteToSend=dataByte;
    52. }
    53.  
    54. }
    55.  
    56. int main(void)
    57.     {
    58.         sei();
    59.     setupIO();
    60.     setTimer(1);
    61.     while(1)
    62.     {  
    63.         sendByte(0x0A);
    64.        
    65.     }
    66. }
    67.  
    68. ISR(TIMER0_COMPA_vect){
    69. if(state==idle)
    70. bit=1;
    71. else{
    72.        
    73.     if(bitCount<8)
    74.     {if(bitCount==-1)
    75.         bit=0;
    76.         else
    77.     bit=(byteToSend<<bitCount)&0x80;
    78.     }
    79.     else{
    80.     bit=1;
    81.     state=idle;
    82.     }
    83.     bitCount++;
    84. }
    85. if(bit){
    86. PORTB|=(1<<PB4); //PB4 being used as Uart pin
    87. }
    88. else{
    89. PORTB&=~(1<<PB4);
    90. }
    91. }
    Receiver code

    Code ( (Unknown Language)):
    1.  
    2. /*
    3.  * tiny45Rx.c
    4.  *
    5.  */
    6. #define F_CPU 1000000
    7. #include <avr/io.h>
    8. #include <avr/interrupt.h>
    9. #include <util/delay.h>
    10. unsigned short byteToSend,bit;
    11. unsigned int timerCount=0;
    12.  
    13. short state=0,idle=0x00,transmitting=0xff,bitCount=0;
    14. unsigned short receivedByte;
    15. unsigned int byte;
    16.  
    17. void setTimer(int on){
    18. if(on){
    19. TCNT0=0;
    20. TIMSK|=(1<<OCIE0A);
    21. TCCR0A|=(1<<WGM01);
    22. OCR0A=25;
    23. TCCR0B|=(1<<CS01)|(1<<CS00);
    24. }
    25. else{
    26. TIMSK&=~(1<<OCIE0A);
    27. TCCR0B&=~((1<<CS01)|(1<<CS00));
    28. }
    29.                 }
    30.  
    31.  
    32. void setupIO(){
    33. DDRB=(1<<PB4);
    34. PORTB|=(1<<PB4);
    35. _delay_ms(500);
    36. PORTB&=~(1<<PB4);
    37. _delay_ms(500);
    38.  
    39. }
    40.  
    41.  
    42. int main(void)
    43.     {
    44.         sei();
    45.     setupIO();
    46.     setTimer(1);
    47.     while(1)
    48.     {  
    49.         if(receivedByte==0x0A)
    50.         PORTB|=(1<<PB4);
    51.         else
    52.         PORTB&=~(1<<PB4);
    53.     }
    54. }
    55.  
    56. ISR(TIMER0_COMPA_vect){
    57.     byte=byte<<1;
    58.    
    59.     if(~PINB&(1<<PB2))
    60.     byte|=1;
    61.    
    62.     if((byte&(0b10<<10))&&(byte&1))
    63.     receivedByte=0x0A;
    64.     else
    65.     receivedByte=0x00;
    66.    
    67. }
    But for some reason it doesn't work , the if(receivedByte==0x0A) section never executes!

    Can you help correct it?
     
  2. tshuck

    Well-Known Member

    Oct 18, 2012
    3,531
    675
    I would suggest limiting your possible sources of error to one device. Meaning, try transmitting to a device you know can receive a UART message and respond properly. That way, you can narrow down the error to a single device, get that one working, and fix the other device if necessary.

    Preferably, transmit to a UART to USB converter and display your receipt using a terminal program (i.e. TeraTerm, PuTTY, Hyperterminal, etc.), that way, you can see what the device is outputting.
     
    allahjane likes this.
  3. MrChips

    Moderator

    Oct 2, 2009
    12,414
    3,353
    If I were writing a soft UART I would do it in assembler so that I have full control of instruction cycle times. I'm sure I've already done this for Atmel AVR.
     
  4. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    allahjane likes this.
  5. allahjane

    Thread Starter Member

    Sep 19, 2012
    75
    1
    Have you done it with 16 or 8 bit timers or by just wasting cycles to create delay?
     
  6. allahjane

    Thread Starter Member

    Sep 19, 2012
    75
    1
  7. John P

    AAC Fanatic!

    Oct 14, 2008
    1,632
    224
    I've done this quite a few times on PIC processors, where the receive function triggers an interrupt when the start bit is seen, then there are 8 successive timer interrupts to grab the data. I wouldn't dream of using a delay instead of a timer interrupt; that would tie up the processor for an outrageously long time.

    Transmitting is easier, because all you need is a succession of timer interrupts. But if you need full-duplex communication, it's a much harder job, and most likely you'd need to keep the baud rate quite low.
     
  8. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Maybe uses 10% of overall processing power of a 2 MIPS chip doing 9600 baud. It is reliable sending and receiving multibyte sequences, obviously as long as there aren't any other interrupts that take too long and mess the timing up.
     
    allahjane likes this.
  9. allahjane

    Thread Starter Member

    Sep 19, 2012
    75
    1
    can you please provide me some code snippets of your implementation :D
     
  10. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Not sure if it's much help to you - it's PIC assembler and not well commented - but here's the interrupt part anyway:
    Code ( (Unknown Language)):
    1.     movlw 0xC5
    2.     movwf TMR0
    3.  
    4.     incf txstatus, W            ;if txstatus=FF then recieve mode
    5.     btfss STATUS, Z
    6.     goto transmit
    7.  
    8.  
    9. receive
    10.     incf rxstatus, W
    11.     btfss STATUS, Z
    12.     goto rxstarted
    13.     btfsc PORTB, 1                ;test rx pin
    14.     goto endint
    15. ;RECIEVE SECTION
    16.     clrf rxstatus                ;start of start bit reception
    17.     movlw 0x04                    ;4 counts after start bit detected
    18.     movwf rx3count                ;is closest to the centre of first data bit
    19.     clrf rxbyte
    20.     goto endint
    21.  
    22. rxstarted                        ;data bits or stop bit
    23.     decf rx3count, F
    24.     btfss STATUS, Z
    25.     goto endint            ;probably not a good idea to transmit and recieve at the same time
    26.  
    27.     movlw 0x03        
    28.     movwf rx3count                    
    29.  
    30.     movlw 0x08            ;stop bit?
    31.     subwf rxstatus, W
    32.     btfsc STATUS, Z
    33.     goto rxstopbit
    34.  
    35.     bcf STATUS, C        ;prepare for next data bit
    36.     rrf rxbyte, F
    37.     btfsc PORTB, 1        ;get current data bit and store
    38.     bsf rxbyte, 7
    39.  
    40.     incf rxstatus, F
    41.     goto endint
    42.  
    43. rxstopbit                    ;about halfway through stop bit is a  
    44.     movf rxbyte, W            ;good time to start waiting for start bit
    45.     movwf rxbytebuffer        ;copy to use in main
    46.     movlw 0xff
    47.     movwf rxstatus
    48.     bsf flags, bytereceived
    49.     goto endint
    50.  
    51.  
    52.  
    53. ;TRANSMIT SECTION
    54. transmit
    55.     decf tx3count, F            ;bits start every 3 interrupts
    56.     btfss STATUS, Z
    57.     goto endint
    58.  
    59.  
    60.     movf PORTBtemp, W            ;putting this here means timing
    61.     movwf PORTB                    ;is always the same. PORTB temp was
    62.     movlw 0x03                    ;set up the last time through
    63.     movwf tx3count
    64.  
    65.     decf txstatus, W            ;using 1 for start transmission
    66.     btfss STATUS, Z
    67.     goto notstartbit
    68.     bcf PORTBtemp, 0            ;start bit
    69.     incf txstatus, F
    70.     goto endint
    71. notstartbit
    72.     movf txstatus, W            ;if txstatus = 0 just waiting for
    73.     btfsc STATUS, Z                ;next transmission
    74.     goto endint
    75.      
    76.  
    77.     sublw d'10'                    ;10th bit is stop bit                    
    78.     btfsc STATUS, Z
    79.     goto txstopbit    
    80.  
    81.     bsf PORTBtemp, 0
    82.     btfss txbyte, 0
    83.     bcf PORTBtemp, 0
    84. bcf STATUS, C
    85.     rrf txbyte, F
    86.     incf txstatus, F
    87.     goto endint
    88.  
    89. txstopbit
    90.     bsf PORTBtemp, 0
    91.     clrf txstatus
    92.  
    93.  
    94. endint
    95.  
     
  11. allahjane

    Thread Starter Member

    Sep 19, 2012
    75
    1
    So, assembly you say!

    Does that mean C code may have unreliable timing?
     
  12. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Shouldn't be a problem to use C, but I don't use it so can't help with that part.
    For something so simple that gets called about 30000 times a second, it might be worth considering assembler for the interrupt. Saving a few clock cycles each time gives the main program more time to work with.
     
  13. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    5,435
    1,305
    This serial TX code below uses C and makes perfect timing because any individual baud error is negated on the next timed baud. One constant (SER_BAUD) is used to set the baudrate;

    Code ( (Unknown Language)):
    1.  
    2. // a bit banged serial function, sends INVERTED serial data
    3. // to PC serial port using just 2 resistors.
    4.  
    5. #define PIN_SER_OUT LATA.F3       // which pin for serial out (PORTA.F3)
    6. #define SER_BAUD 51               // TMR1 (1Mhz/19200 baud) = 52
    7.                                   // tested; works from 49 to 53, using 51
    8.  
    9. //---------------------------------------------------------
    10. void send_serial_byte(unsigned char data)
    11. {
    12.   // this manually sends a serial byte out any PIC pin.
    13.   // NOTE! serial is inverted to connect direct to PC serial port.
    14.   // baud timing is done by using TMR1L and removing
    15.   // timer error after each baud.
    16.   unsigned char i;
    17.  
    18.   i=8;                            // 8 data bits to send
    19.  
    20.   PIN_SER_OUT = 1;                // make start bit
    21.   TMR1L = (256 - SER_BAUD);       // load TMR1 value for first baud;
    22.   while(TMR1L.F7);                // wait for baud
    23.  
    24.   while(i)                        // send 8 serial bits, LSB first
    25.   {
    26.     if(data.F0) PIN_SER_OUT = 0;  // invert and send data bit
    27.     else    PIN_SER_OUT = 1;
    28.  
    29.     data = (data >> 1);           // rotate right to get next bit
    30.     i--;
    31.     TMR1L -= SER_BAUD;            // load corrected baud value
    32.     while(TMR1L.F7);              // wait for baud
    33.   }
    34.  
    35.   PIN_SER_OUT = 0;                // make stop bit
    36.   TMR1L -= SER_BAUD;              // wait a couple of baud for safety
    37.   while(TMR3L.F7);
    38.   TMR1L -= SER_BAUD;
    39.   while(TMR1L.F7);
    40. }
    41. //---------------------------------------------------------
    42.  
    That code assumes the 8bit timer TMR1L is running at 1MHz. You should be able to use any 8bit timer if you set the prescaler and set the SER_BAUD variable.

    Also if you need to change to non-inverted data, change this;
    if(data.F0) PIN_SER_OUT = 0; // invert and send data bit
    else PIN_SER_OUT = 1;
    to this;
    if(!data.F0) PIN_SER_OUT = 0; // send data bit
    else PIN_SER_OUT = 1;

    This web page also has a serial RX function in C using the same self-correcting baud timing;
    http://www.romanblack.com/bitbangserial.htm

    Note! Interrupts are not needed or used. However you might want to use an "interrupt on pin change" to detect the start of a serial receive operation.
     
  14. MrChips

    Moderator

    Oct 2, 2009
    12,414
    3,353
    Here are some code snippets for a SW UART

    Code ( (Unknown Language)):
    1.  
    2.  
    3. *********************************************************
    4. *         Register Definitions
    5. *********************************************************
    6.  
    7. A         EQU       16
    8. B         EQU       17
    9. C         EQU       18
    10. timcnt    EQU       19
    11. flags     EQU       20
    12. DLO       EQU       21
    13. DHI       EQU       22
    14.  
    15. ;SCI registers
    16. rxdata    EQU       23
    17. txdata    EQU       24
    18. txcnt     EQU       25
    19.  
    20. ;index registers
    21. Z         EQU       30
    22. ZHI       EQU       31
    23.  
    24. *********************************************************
    25. *         Software SCI
    26. *********************************************************
    27.  
    28. ;int0 (PB2) interrupt service
    29. ;rxdata start bit detected
    30. int0      MOV       saveA A             ;save A
    31.           IN        saveSR SREG         ;save SR
    32.           CLR       rxdata
    33.           OUT       GIMSK rxdata        ;disable int1 and int0
    34.           LDI       A SEMIBAUD
    35.           OUT       TCNT0 A
    36.           LDI       A,$01
    37.           OUT       TCCR0 A             ;start timer0
    38.           OUT       SREG saveSR         ;restore SREG
    39.           MOV       A saveA             ;restore A
    40.           RETI
    41.  
    42. ;timer0 overflow interrupt service
    43. ;timer0 is used by both tx and rx service
    44. ;only half-duplex operation is allowed
    45. ;only one of tx or rx must be active
    46.  
    47. ;rxdata is accumulated @ 9600 baud
    48. ;flags RDA is set to 1 when start bit = 1 is shifted out
    49. t0ovf     MOV       saveA A             ;save A
    50.           IN        saveSR SREG         ;save SR
    51.           SBRS      flags TXCLK
    52.           JMP       rcvr
    53. ;transmitter is active
    54.           DEC       txcnt
    55.           BNE       t0ovf1
    56. ;end of transmission
    57.           SBI       PORTB TD0
    58.           CBI       PORTB TD
    59.           CLR       A
    60.           OUT       TCCR0 A
    61.           CBR       flags TXmask
    62.           JMP       t00
    63. ;send next bit
    64. t0ovf1    SEC                 ;stop bit is 1
    65.           ROR       txdata
    66.           BCS       t0hi
    67.           CBI       PORTB TD0
    68.           SBI       PORTB TD
    69.           JMP       t01
    70. t0hi      SBI       PORTB TD0
    71.           CBI       PORTB TD
    72.           NOP
    73. t01       LDI       A BAUD
    74.           OUT       TCNT0 A
    75.           JMP       t00
    76.  
    77. ;receiver is active
    78. ;data received is inverted
    79. ;so that start bit = 1
    80. ;no checks on start or stop bits
    81. ;flags RDA bit is asserted during the middle of the MSB (parity bit)
    82. rcvr      CLC
    83.           SBIS      PINB RD   ;test RD input
    84.           SEC
    85.           ROR       rxdata
    86.           BCS       rxdone    ;search for start bit = 1
    87.           LDI       A BAUD
    88.           OUT       TCNT0 A
    89.           JMP       t00
    90. ;rxdata received
    91. rxdone    COM       rxdata    ;now flip data
    92.           CLR       A
    93.           OUT       TCCR0 A   ;stop timer
    94.           SBR       flags RDAmask
    95. t00       MOV       A saveA             ;restore A
    96.           OUT       SREG saveSR         ;restore status
    97.           RETI
    98.  
    99. ;put character
    100. ;enter with A = 8-bit char
    101. ;destroyed: A, txdata, txcnt
    102. putc      TST       txcnt               ;wait for end of transmission
    103.           BNE       putc
    104.           MOV       txdata A
    105.           LDI       txcnt 10
    106.           SBR       flags TXmask        ;set tx clock flag
    107.           CBI       PORTB TD0           ;start bit = 0
    108.           SBI       PORTB TD            ;complementary start bit
    109.           LDI       A BAUD
    110.           OUT       TCNT0 A
    111.           LDI       A $01
    112.           OUT       TCCR0 A             ;start timer0
    113.           RET
    114.  
    115.  
     
Loading...