UART AVR receiving corrupt data

I am having issues with corrupt data I suspect but honestly I am not sure. I am trying to automate a testing sequence for a stepper motor acceleration profile on the Arduino atmega 328p using a python script. I am using serial uart with a ring buffer for my communication between the Arduino. The testing sequence is that the PC will send a table of time delays to the Arduino and than a benchmark test is preformed using interrupts and timers to measure the speed of a stepper motor and sends the speed back to the PC, which I want to do an optimization on using a genetic algorithm. The issue I am having is the script and test run perfect for the first couple of runs, but at around 4+ iterations of the test the system breaks and the stepper motor starts missing steps, which leads me to believe that its buffer overflow but I also tried backing the buffer larger to test that and had zero change in the behavior. Any help or suggestions would be greatly apricated. I have put the C code followed by the python script below.

C program:

#include <avr/interrupt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "usart.h"
#include "ringBuffer.h"

volatile bool command;      /* Command line active? */
volatile bool quit_early;   /* Abort processing. */

#define TRUE    1
#define FALSE   0

typedef uint8_t rbuf_data_t;
typedef uint8_t rbuf_count_t;

rbuf_t  rbuf;
char line[BUFFER_SIZE];

#define debounceDelay 20
#define dutyCycle 0x80  //80 -> 50% C0 -> 75%

#define aluminum 1
#define steel    2
#define white    3
#define black    4

// steps for angles
#define rotate90  50    // number of steps per 90 degrees rotation
#define rotate180 100   // number of steps per 180 degrees rotation

// DC motor directions
#define MOTOR_CW  0b00001011
#define MOTOR_CCW 0b00000111
#define MOTOR_BRK 0b00001111

// ADC item threshold values
#define Ai_max 255
#define Stl_max 700
#define Wht_max 955
#define Blk_max 1023


#define USART_RECEIVE_BUFFER_SIZE 80
unsigned char g_receiveBuffer [ USART_RECEIVE_BUFFER_SIZE ];
unsigned char g_receiveHead ;
unsigned char g_receiveTail ;
//ADC
volatile int g_ADC_min;


/* flags for sensors
 * bucket calibration flag = sensorFlag 0x01 <---- buck calibration flag  
 * EOT flag                = sensorFlag 0x02 <---- end of travel flag
 * Pause state flag        = sensorFlag 0x04 <---- pause button
 * Optical sensor          = sensorFlag 0x08 <---- optical sensor for ADC trigger
 */
volatile unsigned char g_statusFlag;

//item positions
volatile int g_cur_item;
volatile int g_prev_item = 4;   // set to calibrated home black item

volatile int g_position = 32000;
 
// Stepper motor variables
const unsigned char g_steptable[4] = {0b00110110,  // full step mode
                            0b00101110, 
                            0b00101101, 
                            0b00110101};
                                    

volatile int g_accprofile50[50] = {
    200,195,185,175,165,155,145,135,125,115,
    80,80,80,80,80,80,80,80,80,80,
    70,70,70,70,70,70,70,70,70,70,
    70,70,70,70,70,70,70,70,70,70,
    110,120,130,140,150,160,170,180,190,200};



volatile int g_accprofile100[100] = {
    180,175,170,165,160,155,150,145,140,135,
    130,125,120,115,110,105,100,90,80,70,
    60,55,55,55,55,55,55,55,55,55,
    55,55,55,55,55,55,55,55,55,55,
    55,55,55,55,55,55,55,55,55,55,
    55,55,55,55,55,55,55,55,55,55,
    55,55,55,55,55,55,55,55,55,55,
    70,70,70,70,70,70,70,70,70,70,
    70,70,70,70,70,70,70,70,70,70,
    110,120,130,140,150,160,170,180,190,200};

void mTimer(int count);

void ioinit(void);
void XINT_init(void);
void stepper_CW (int s);
void stepper_CCW(int s);
void home_Bucket(void);
void motorSpeedtest();
void updateLCD(void);
void ADC_init(void);
void mTimer(int count);
void uTimer(int count);

FILE uart_str = FDEV_SETUP_STREAM(UART_putByte, UART_getByte, _FDEV_SETUP_RW);
#define START_TIMER3 TCCR3B = (1<<CS31)|(1<<WGM32)
#define STOP_TIMER3  TCCR3B = 0
#define CLEAR_TIMER3 TCNT3 =  0
 
volatile int ret;
volatile unsigned int timer3count;
volatile unsigned int results;

// Parameters
volatile bool command;      /* Command line active? */
volatile bool quit_early;   /* Abort processing. */


char line[BUFFER_SIZE];



void loadData()
{
    ret = fscanf(stdin," %3s", line);
    if (ret != 1 || ret == EOF || feof(stdin) || ferror(stdin))
    {
        // Error handling
        printf("Something went wrong!\r\n");
    }
}

int main(void)
{
    ioinit();
    UART_init();
    stdout = stdin = &uart_str;
    sei();
    int num = 0;
    char c;
    printf("\n Ready for input\n");
    while (1) 
    {   
            command = TRUE;
            c = fgetc(stdin);
            switch(c)
            {
                case 'L':
                    printf("loading speed data\n");
                    for(int i = 0; i < 50; i++)
                    {
                        loadData();
                        num = parsedigits(line);
                        
                        if( num == -1)
                        {
                            printf("error loading numbers\n");
                        }
                        g_accprofile50[i] = num;
                        strcpy(line, "");
                    }
                    printf("table loaded\n");
                    break;
                // load table data
                case 'T':       
                    printf("starting speed test\n");
                    motorSpeedtest();
                    while((timer3count < 900) && ((g_statusFlag&0x10)));
                    printf(ACK);
                    printf("%d\n", timer3count);
                    // possible a another break trying fall through
                    printf("Finished test");
                    break;
                case 'H':
                    printf("homing bucket\n");
                    home_Bucket();
                    break;
                default:
                    printf("Invalid command");
            }
    }
}

// bucket calibration using HALL sensor
ISR(INT1_vect){
    g_statusFlag |= 0x01;
    if((g_statusFlag&0x10)){
        STOP_TIMER3;
        g_statusFlag &= (~0x10);
    }
}

ISR(TIMER3_COMPA_vect){
    timer3count++;
}//endTIM3ISR
// interrupt will be triggered at end of ADC

ISR(ADC_vect){
    if(ADC < g_ADC_min){                            // check ADC Vs ADC_min
        g_ADC_min = ADC;
    }
    if((PIND & 0x04) == 0x04){                  // optical sensor
        ADCSRA |= (1<<ADSC);                    // start ADC
    }
}


ISR(USART0_RX_vect){
    unsigned char c = UDR0;
    /* If command line is active, store the character. */
    if (command)
        rbuf_insert(&rbuf, (rbuf_data_t) c);
    else {  /* Otherwise check to see if we need to abort. */
     if (0x03 == c)
        quit_early = TRUE;
    }
}
// <---------------------------------------- Miscellaneous Driver-------------------------------------->
void ioinit(){
    // Configure system clock
    CLKPR = (1<<CLKPCE);        // enable clock change
    CLKPR = (1<<CLKPS0);        // pre-scale system clock from 16MHz to 8MHz
    // Timer 1 configuration for millisecond delay timer
    TCCR1B = (1<<WGM12)|                // Set the waveform generation mode bit description to clear timer
             (1<<CS11);             // Set CS pre-scale to 1/8
    OCR1A = 0x03E8;                     // Set output to compare register for 1000 cycles = 1ms
    TCNT1 = 0x0000;                     // set initial value of timer counter to 0x0000;
    // timer 4 confutation for microsecond delay timer
    TCCR4B = (1<<WGM42)|
             (1<<CS41);                 // Set the waveform generation mode bit description to clear timer
    OCR4A = 0x0064;                     // Set output to compare register for 100 cycles = 0.1ms
    TCNT4 = 0x0000;                     // set initial value of timer counter to 0x0000;
    
    // Configure PORT pins:
    DDRA = 0x7F;    // Configure PORT A pins for Stepper motor control (bucket)
    DDRB = 0xFF;    // Configure PORT B pins for motor control (conveyor)
    DDRC = 0xFF;    // Configure PORT C for LCD output
    DDRD = 0xF0;    // Configure Port D pin to input for INT1,INT2,INT3
    DDRE = 0xC0;    // Configure PORT E pin to input for INT4,INT5
    DDRF = 0x00;    // Configure PORT F pin to input for ADC
    DDRL = 0xFF;
    // initialize miscellaneous functions

    //ADC_init();           
    XINT_init();    // Set up the external Interrupts 1,2,3,4,5
    sei();          // Enable all interrupts
    
    
    //home_Bucket();    // calibrate bucket to black
}
// Configure External interrupts
void XINT_init(){
    EIMSK = (1<<INT1);      //enable INT1 for motor bench test
    EICRA = (1<<ISC11);     // set falling edge for INT 1

}
// initialize ADC
void ADC_init(){
    //Configure ADC set for in A1 ADC1
    ADCSRA = (1<<ADEN)|     // enable ADC & interrupts of ADC
             (1<<ADIE)|
             (1<<ADPS2)|    // Set pre-scaler for ADC
             (1<<ADPS0);
    ADMUX =  (1<<REFS0)|        // Set ADC reference voltage REF:01 & MUX to MUX1 for ADC1
             (1<<MUX0);
}
// calibrate bucket to home black
void home_Bucket(){
    while((g_statusFlag&0x01)==0){
        PORTA = g_steptable[g_position++%4];
        mTimer(20);
    }
    
}

// Timer 1 configured for millisecond timer delay function
void mTimer(int count){
    TIFR1 = (1<<OCF1A);             // Clear timer interrupts flag and start new timer
    while(count--){
        while(!(TIFR1 & 0x02));
        TIFR1 = (1<<OCF1A);     // clear interrupt flag
    } // end while
}// milliTimer

// Timer 4 configured for microsecond timer delay function
void uTimer(int count){
    TIFR4 = (1<<OCF4A);             // Clear timer interrupts flag and start new timer
    while(count--){
        while(!(TIFR4 & 0x02));
        TIFR4 = (1<<OCF4A);     // clear interrupt flag
    } // end while
}// microTimer

// Timer 3
void calibrationtime(){
    START_TIMER3;                       // Set prescaler value to 1/8 clk
    
    TCNT3 = 0x0000;                     // set initial value of timer counter to 0x0000;
    timer3count = 0;
    results = 999;
    OCR3A = 0x3E8;
    //OCR3A = 0x0064;
    TIMSK3 = (1<<OCIE3A);
    g_statusFlag |= 0x10;
}

// Clockwise stepper controller
void stepper_CW(int s)
{
    calibrationtime();
    register int i,j;
    j = s;
    i = 0;
    while(s--)
    {
        PORTA = g_steptable[g_position++%4];
        if(j <= 50)
        {
            uTimer(g_accprofile50[i++]);
        }else
        {
            uTimer(g_accprofile100[i++]);
        }
    }// endwhile
}// end stepper_fwd

void motorSpeedtest(){
    g_statusFlag&= (~0x01);
    home_Bucket();
    for(int i = 0; i < 50; i++)
    {
        PORTA = g_steptable[--g_position%4];
        mTimer(20);
    }
    stepper_CW(50);
}

// Clockwise stepper controller
void stepper_CCW(int s)
{
    register int i,j;
    j = s;
    i = 0;
    while(s--)
    {
        PORTA = g_steptable[--g_position%4];
        if(j <= 50)
        {
            uTimer(g_accprofile50[i++]);
        }else
        {
            uTimer(g_accprofile100[i++]);
        }
    }// endwhile
}// end stepper_fwd
#include "usart.h"
#include "ringBuffer.h"

rbuf_t  rbuf;
void UART_init()
{
    rbuf_init(&rbuf);
    // set baudrate register
    UBRR0 = BRC;
    // enable RX und TX with interrupts
    UCSR0B = (1<<TXEN0)|(1<<RXEN0)|(1<<RXCIE0);
    // set mode to 8 bit 1 stop bit
    UCSR0C = (1<<UCSZ00)|(1<<UCSZ01);
}

int UART_putByte(unsigned char data, FILE *stream)
{
    if('\a' == data){
        fputs("*ring\n", stderr);
        return -1;
    }
    if('\n' == data) UART_putByte('\r',stream);
    /* Wait for empty transmit buffer */
    while (!( UCSR0A & (1<<UDRE0)) );
    /* Put data into buffer, sends the data */
    UDR0 = data;
    return 1;
}


int UART_getByte(FILE *stream)
{   
    unsigned char c;
    // block until something tere
    while(rbuf_isempty(&rbuf));
    /* Wait for data to be received */
    c = rbuf_remove(&rbuf);
    /* Get and return received data from buffer */
    return c;
}

int parsedigits(const char* data){
    char tmp[3];
    
    if(NULL == data) return -1;
    
    for(int i = 0; i < strlen(data); i++)
    {
        tmp[i] = (data[i] - '0');
    }
    return ((100*tmp[0])+(10*tmp[1])+tmp[2]);
}
void rbuf_init(rbuf_t* const buffer)
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        buffer->in    = buffer->buffer;
        buffer->out   = buffer->buffer;
        buffer->count = 0;
    }
    return;
}


rbuf_count_t rbuf_getcount(rbuf_t* const buffer)
{
    rbuf_count_t count;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        count = buffer->count;
    }
    return count;
}


bool rbuf_isempty(rbuf_t* buffer)
{
    return (rbuf_getcount(buffer) == 0);
}


void rbuf_insert(rbuf_t* const buffer, const rbuf_data_t data)
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        if (buffer->count == sizeof(buffer->buffer)) // if the buffer is full..
        return;
        if ((buffer->in) == &buffer->buffer[BUFFER_SIZE-1]) // if the pointer at the top
        buffer->in = buffer->buffer; // wrap around
        *buffer->in = data;
        ++buffer->in;
        buffer->count++;
    }
    return;
}

rbuf_data_t rbuf_remove(rbuf_t* const buffer)
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 
    {
        rbuf_data_t data = *buffer->out;

        if (++buffer->out == &buffer->buffer[BUFFER_SIZE-1])
            buffer->out = buffer->buffer;
        buffer->count--;
        return data;
    }
}

Python script:

# Importing Libraries
import serial
import time
import logging
import random
from data2excel import load_table, load_results


arduino = serial.Serial(port='COM5', baudrate=9600, timeout=0.5)


def write_read(inputCommand, testID):
    # inputCommand = input("\n Enter you command\n    \
    #                   L - load motor speed table\n\
    #                   T - Test motor speed table\n\
    #                   H - Home stepper\n\
    #                   Q - end program\n")
    if inputCommand == "L":
        load_mtrTable(testID)
        #write_read()
    elif inputCommand == "T":
        test_mtrTable(testID)
        #write_read()
    elif inputCommand == "H":
        arduino.write(b"H")
        recv = arduino.read_until("\n").decode("utf-8")
        print(f"received data: {recv}")
        #write_read()
    elif inputCommand == "Q" or inputCommand == "q":
        arduino.write(b"Q")
        print("Program Exiting")
        time.sleep(0.1)
        #arduino.close()
    else:
        print("Invalid input.")
        #write_read()
"""
 This functions sends the cmd to the arduino to enter the load stepper speed
 table state.
 @parm: testID is a unque ID to distinguish between each test table and result
 @return: None
"""
def generate_table(size):
    table = [str(random.randint(70,220)).zfill(3) for _ in range(size)]

    return table


def waitfor(this):
    line = arduino.read(2).decode("utf-8")
    while(this not in line): 
        line = arduino.read(2).decode("utf-8")
    return line
"""
 This functions sends the cmd to the arduino to enter the load stepper speed
 table state.
 @parm: testID is a unque ID to distinguish between each test table and result
 @return: None
"""
def load_mtrTable(test_ID):
        print("<---------------------sending motor data--------------------->")
        # write T to arduino to init test squecence
        arduino.write(b"L")
        recv = arduino.read_until("\n").decode("utf-8")
        print(f"received data: {recv}")
        # wait for arduino to respond with R for ready
        print("init table loading")
        MTR_table = generate_table(50)
        load_table(MTR_table,test_ID)
        # start sending speed table
        for num in MTR_table:
            arduino.write(num.encode("utf-8"))
            # wait for arduino to acknowledgement
        recv = arduino.read_until("\n").decode("utf-8")
        print(f"received data: {recv}")

"""
 This functions sends the cmd to the arduino to enter the load stepper speed
 table state.
 @parm: testID is a unque ID to distinguish between each test table and result
 @return: None
"""
def test_mtrTable(testID):
    print("PC:starting test sequence")
    arduino.write(b"T")
    recv = arduino.read_until("\n").decode("utf-8")
    print(f"received data: {recv}")
    waitfor("A")
    results = arduino.read(4).decode("utf-8")
    load_results(results,testID)
    print(results)
    recv = arduino.read_until("\n").decode("utf-8")
    print(f"received data: {recv}")
    print("<---------------------end of test----------------->")


if __name__ == "__main__":

    while True:
        time.sleep(2)
        for i in range(0,5):
            write_read("L",i)
            write_read("T",i)


Read more here: https://stackoverflow.com/questions/67008229/uart-avr-receiving-corrupt-data

Content Attribution

This content was originally published by Kurt Elliott at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: