Author Topic: Uart bit banging code  (Read 4514 times)

0 Members and 1 Guest are viewing this topic.

Offline Vindhyachal.taknikiTopic starter

  • Frequent Contributor
  • **
  • Posts: 487
Uart bit banging code
« on: April 07, 2016, 01:52:50 am »
1. I have a application in which uart is done by software since it has only one uart & it is used for another purpose.
MCU is 16Mhz with 62.5ns instruction time. Goal is to continuously receive data on software uart at 9600 baud & transmit it on hardware uart at 57600 baud.

2. Below is code for above task. Delay I have made from timer to be as accurate as possible.

3. Is there any way I can best optimize code for this task. One thing I am tryign to do is to move tranmit section on hardware uart in interrupt to have least possible time in main.

4. Is this code ok for software uart?

Code: [Select]
#define SYSTEM_FREQ_HZ   16000000
#define BAUD_RATE        9600

#define DELAY_US_1_5_BIT    ((16000000 / 9600) * 1.5)   //approx 2500us
#define DELAY_US_1_0_BIT    ((16000000 / 9600) * 1.0)   //approx 1666.67us
#define DELAY_US_0_5_BIT    ((16000000 / 9600) * 0.5)   //approx 833.33us

void rx_byte(void)
{
    uint8_t data_val;
    uint8_t cnt;
    uint8_t err;

/* pin as input pull up */
    RX_PIN_INPUT_PULLUP();

    while(1)
    {
        data_val = 0U;
        err = 0U;
       
    /* while pin is high */
        while(1U == READ_PIN());
       
    /* 1.5 bit delay */
        delay(DELAY_US_1_5_BIT);
       
    /* get byte */
        for(cnt = 0U ; cnt < 8U ; cnt++)
        {
            if(1U == READ_PIN())
            {
                data_val = data_val | (1U << cnt);
            } 
           
            /* 1 bit delay */
            delay(DELAY_US_1_0_BIT);         
        } 
       
    /* stop bit error low */
        if(0U == READ_PIN())
        {
            err = 1U;
        } 
       
    /* 0.5 bit delay */
        delay(DELAY_US_0_5_BIT);     
 
       
    /* send byte on another uart */
        if(0U == err)
        { 
            uart_tx_data(data_val);
        }
    }
}
 

Offline EPTech

  • Regular Contributor
  • *
  • Posts: 168
  • Country: be
    • EP Technical Services
Re: Uart bit banging code
« Reply #1 on: April 07, 2016, 09:30:08 am »
Hi There,

The uart transmit actually only takes a few instructions to put the data in the UART transmit buffer, so putting that in an interrupt won't do you much good. Getting rid of the software timers will save you a lot of time in the main though.
I would define a hardware timer that does a least 2x oversampling of the bit clock on the Rx. Then you can sample the bits in the interrupt routine of that timer. So you need to keep a clock counter and check the current level of Rx against the previous level.
To keep everything in sync, you can use an interrupt on change of the Rx pin to detect the start bit and enable the sampling in the other interrupt routine using a global enable, ex. putting the clock counter on 1. Do not forget to disable the interrupt on change while you are sampling. Other wise it will reset the clock counter each time it detects a change in Rx.

Hope this helps.
Kind greetings,

Pascal.
 

Offline Vindhyachal.taknikiTopic starter

  • Frequent Contributor
  • **
  • Posts: 487
Re: Uart bit banging code
« Reply #2 on: April 08, 2016, 04:15:31 am »
One error is , calculation of delay one bit time, it should be (1000000/9600), not (system_freq/9600).
So one bit delay is = (1000000/9600) = 104.17us
 

Offline obiwanjacobi

  • Frequent Contributor
  • **
  • Posts: 988
  • Country: nl
  • What's this yippee-yayoh pin you talk about!?
    • Marctronix Blog
Re: Uart bit banging code
« Reply #3 on: April 08, 2016, 06:44:19 am »
Take a look at this code: https://github.com/PaulStoffregen/AltSoftSerial

It uses hardware timers to time the bits...

[2c]
Arduino Template Library | Zalt Z80 Computer
Wrong code should not compile!
 

Offline Vindhyachal.taknikiTopic starter

  • Frequent Contributor
  • **
  • Posts: 487
Re: Uart bit banging code
« Reply #4 on: April 09, 2016, 05:20:37 pm »
I have tried by making rx from 9600 baud uart by software & then when rx is complete send at once at tx of 115200 uart.
I tested it but after few bytes code fails. Majorly after 2 bytes or there after. However if one byte is sent & delay b/w two bytes then codes fine.

Meanwhile now some specs are changed. Now I have to do bidirectional uart. Let say uart1 is with 9600 baud & uart2 is with 115200 baud.
Now data rx on uart1 is to be sent on uart2 & data rx on uart2 is to be sent on uart1.
Maximum data size to be tx or rx is 250KB-500kB. (small video)

Now i have selected a MCU with 2hardware uart.
I will try by making two hardware uart, one with 9600 & other with 115200 baud. Each uart has interrupt for rx only. When data is rx on any uart, in ISR it will send it immediately to other uart inside ISR.

But I dont think this will work without data buffer. How to calculate optimal buffer size for this application.
 

Offline Vindhyachal.taknikiTopic starter

  • Frequent Contributor
  • **
  • Posts: 487
Re: Uart bit banging code
« Reply #5 on: April 12, 2016, 07:08:14 am »
I have made below code. Task is to transmit data to/from two uarts.
One uart is at 9600 baud & other is at 115200 baud.
Whatever data is received at one uart is to be sent to other uart & vice versa.
MCU is 32 bit cortex M3 with two hardware uart's & core is running at 16Mhz.
Have implemented two circular buffers , each of size 27K. Data length could be upto 500KB in one go.
Data is always 1 strt,8-data & 1 stop.

1. Currently I have roughly taken 27K buffer. But how to calcualte how much buffer exactly should be required.
For example, in case of 9600 baud, I will rx 960 bytes of data in 1 sec & 11520 bytes in other uart.

2. Initillay only rx int of both uarts are enabled. Tx int are enabled only when there is data in buffer, & disabled again when head=tail.

Code: [Select]
const uint32_t g_arr_size = 27648U;
volatile uint8_t g_9600_arr[g_arr_size];
volatile uint32_t g_9600_head;
volatile uint32_t g_9600_tail;

volatile uint8_t g_115200_arr[g_arr_size];
volatile uint32_t g_115200_head;
volatile uint32_t g_115200_tail;



void init_both_uarts(void)
{
    uart2_9600_baud();
    uart2_rx_int_enable();
    uart2_tx_int_disable();

    uart1_115200_baud();
    uart1_rx_int_enable();
    uart1_tx_int_disable();   
   
}

void uart1_isr(void)
{
    uint16_t data;
    uint32_t next_head;
   
/* if data is rx */
    if(uart1_data_rx_nt_full())
    {   
    /* read data */
        data = READ_UART1_DATA();
   
    /* store data in array */   
        /* inc the head cnt */
        next_head = g_115200_head++;
        if(next_head >= g_arr_size)
        {
            next_head = 0U;
        }   
   
        /* check if there is room */
        if(next_head != g_115200_tail)
        {
            g_115200_arr[g_115200_head] = data;
            g_115200_head = next_head;
       
            /* enable other uart interrupt */
            ENABLE_UART2_INT_TX();
        }   
        else
        {
            /* diacrd the byte */
        }   
    }
 
/* if data is tx */
    if(uart1_data_tx_empty())
    {   
    /* remove the byte from array */
        if(g_9600_head != g_9600_tail)
        {
            while( !UART2_WHILE_TXE_COMPLETE );
            WRITE_UART2_DR_REG( g_9600_arr[g_9600_tail] ); 

            g_9600_tail++;
            if(g_9600_tail >= g_arr_size)
            {
                g_9600_tail = 0U;
            }   
        }   
    /* disable uart int tx */
        else
        {
            DISABLE_UART1_INT_TX();
        }   
    }   
       
}


void uart2_isr(void)
{
    uint16_t data;
    uint32_t next_head;
   
/* if data is rx */
    if(uart2_data_rx_nt_full())
    {   
    /* read data */
        data = READ_UART2_DATA();
   
    /* store data in array */   
        /* inc the head cnt */
        next_head = g_9600_head++;
        if(next_head >= g_arr_size)
        {
            next_head = 0U;
        }   
   
        /* check if there is room */
        if(next_head != g_9600_tail)
        {
            g_9600_arr[g_9600_head] = data;
            g_9600_head = next_head;
       
            /* enable other uart interrupt */
            ENABLE_UART1_INT_TX();
        }   
        else
        {
            /* diacrd the byte */
        }   
    }
 
/* if data is tx */
    if(uart2_data_tx_empty())
    {   
    /* remove the byte from array */
        if(g_115200_head != g_115200_tail)
        {
            while( !UART1_WHILE_TXE_COMPLETE );
            WRITE_UART1_DR_REG( g_115200_arr[g_115200_tail] ); 

            g_115200_tail++;
            if(g_115200_tail >= g_arr_size)
            {
                g_115200_tail = 0U;
            }   
        }   
    /* disable uart int tx */
        else
        {
            DISABLE_UART2_INT_TX();
        }   
    }   
       
}
 

Offline obiwanjacobi

  • Frequent Contributor
  • **
  • Posts: 988
  • Country: nl
  • What's this yippee-yayoh pin you talk about!?
    • Marctronix Blog
Re: Uart bit banging code
« Reply #6 on: April 12, 2016, 08:14:49 am »
You don't need a full buffer for the fast Uart. Because the difference is so great between fast and slow Uarts, any byte send to the fast Uart will probably already be sent by the time the next byte arrives.
If you're using some sort of message protocol I would make this code at least parse the headers and determine the size of these messages in order to sent all bytes of a message in one go - if that matters for the receiver at the other end.
The buffer of the slow Uart must be big enough to hold all the bytes of the largest message (sequence) sent to the fast uart. If the sender connected to the fast uart emits a continuous stream of data it will never work because the slow uart will never be able to catch up.

So, much depends on how you will use this and what kind of data is send over these channels.
[2c]
« Last Edit: April 12, 2016, 08:17:38 am by obiwanjacobi »
Arduino Template Library | Zalt Z80 Computer
Wrong code should not compile!
 

Offline Vindhyachal.taknikiTopic starter

  • Frequent Contributor
  • **
  • Posts: 487
Re: Uart bit banging code
« Reply #7 on: April 13, 2016, 05:02:32 am »
There were small in my last codes. Correcting that code worked. Two errors, one was increment the head & other was sending data.
Now code is working ok.
Regrading buffer, I understand buffer will get full in case of 115200 baud in 3sec, for that SRAM is required or data on 115200 should be in breaks.
Otherwise above code, where everything is implemented in interrupts in working fine for samll amount of data.

Thanks for help/comments.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf