ATmega328P USART with interrupts freeze

Thread Starter

kadawn

Joined Oct 3, 2018
7
Hi,

I'm new to this forum, so I'll do my best to describe my problem:
The code below uses interrupts to receive the longest strings possible. It puts its content inside a linked list buffer, then transfers that content to a second linked list buffer when it receives a EOL, and finally transmits the content of this last buffer.
My problem is that when I send +20 long strings, the USART hangs. ¿Is there a way to optimize this code and solve that problem? I think it may be related to an incompatible pointer type in the functions byerx and byetx that erase each linked list.
Note: The linked lists are defined in a header file, and the code itself is started from the execution of USART_Init. I'm using PuTTY for Windows to test the USART.
Code:
// includes
#define F_CPU    16000000UL        // MCU Clock Speed - needed for baud rate value computation
#define TX_BUFFLEN 30            // Send buffer size
#define RX_BUFFLEN 30            // Receive buffer size


// includes
#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdlib.h>

#include "USART_implement_me.h"

char rx_buffer[10];

volatile uint8_t portbhistory = 0;     // default is high because the pull-up


void pushtx(volatile struct node_tx ** head, int val) {
    /* now we can add a new variable */
    struct node_tx * new_node = (struct node_tx*)malloc(sizeof(struct node_tx));
    volatile struct node_tx *last = *head;  /* used in step 5*/

    /* 2. put in the data  */
    new_node->val  = val;

    /* 3. This new node is going to be the last node, so make next
          of it as NULL*/
    new_node->next = NULL;

    /* 4. If the Linked List is empty, then make the new node as head */
    if (*head == NULL)
    {
       *head = new_node;
       return;
    }
  
    /* 5. Else traverse till the last node */
    while (last->next != NULL)
        last = last->next;

    /* 6. Change the next of last node */
    last->next = new_node;
    return;
}
void pushrx(volatile struct node_rx ** head, int val) {
    /* now we can add a new variable */
    struct node_rx * new_node = (struct node_rx*)malloc(sizeof(struct node_rx));
    volatile struct node_rx *last = *head;  /* used in step 5*/

    /* 2. put in the data  */
    new_node->val  = val;

    /* 3. This new node is going to be the last node, so make next
          of it as NULL*/
    new_node->next = NULL;

    /* 4. If the Linked List is empty, then make the new node as head */
    if (*head == NULL)
    {
       *head = new_node;
       return;
    }
  
    /* 5. Else traverse till the last node */
    while (last->next != NULL)
        last = last->next;

    /* 6. Change the next of last node */
    last->next = new_node;
    return;
}



// The initialisation function. Call it once from your main() program before
// issuing any USART commands with the functions below!
//
// Call it at any time to change the USART communication parameters.
//
// Returns zero in case of success, non-zero for errors.
uint8_t USART_Init(struct USART_configuration config)
{
    DDRD = (1 << DDB5);

    // You can recycle your previous code. But remember, this time you are
    // supposed to configure interrupts! Maybe you]ll have to extend it a little bit?
    unsigned int ubrr = F_CPU/16/config.baudrate-1;
    /*Set baud rate */
    UBRR0H = (unsigned char)(ubrr>>8);
    UBRR0L = (unsigned char)ubrr;
    /*Enable receiver and transmitter */
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    /* Set frame format: 8data, 2stop bit */

    DDRB &= ~(1 << DDB0);     // Clear the PB0 pin
    // PB0 (PCINT0 pin) is now an input

    PORTB |= (1 << PORTB0);    // turn On the Pull-up
    // PB0 is now an input with pull-up enabled


    PCICR |= (1 << PCIE0);    // set PCIE0 to enable PCMSK0 scan
    PCMSK0 |= (1 << PCINT0);  // set PCINT0 to trigger an interrupt on state change

    //UCSR0B |=
    /* interrupt on receive */
    //(1 << TXCIE0);
    UCSR0B |=
    ///* interrupt on send */
    (1 << RXCIE0);
    UCSR0B |=
    ///* interrupt on send */
    (1 << UDRIE0);

    volatile struct node_rx * headd = NULL;
    headd = (struct node_rx*)malloc(sizeof(struct node_rx));
    if (headd == NULL) {
        USART_Transmit_char('e');
        return 1;
    }
    //headd->val = ' ';
    //headd->next = NULL;

    volatile struct node_tx * head = NULL;
    head = (struct node_tx*)malloc(sizeof(struct node_tx));
    if (head == NULL) {
        USART_Transmit_char('e');
        return 1;
    }
    //head->val = ' ';
    //head->next = NULL;

    if (config.databits == 8)
    {
        UCSR0C |= (1<<USBS0);
    }
    else if (config.databits == 7){
        UCSR0C |= (0<<USBS0);
    }
    else if (config.databits == 9){
        //Activates 9th data bit
        UCSR0C |= (1<<USBS0);
        UCSR0B |= (1 << UCSZ02);
    }
    else {
        return 1;
    }
    if (config.stopbits == 1){
        UCSR0C|= (1<<UCSZ00);
    }
    else if (config.stopbits == 2) {
        UCSR0C|= (3<<UCSZ00);
    }
    else {
        return 1;
    }
    if (config.parity == 1) {
        UCSR0C |= (1 << UPM01);
    }
    else if (config.parity == 2) {
        UCSR0C |= (1 << UPM01);
        UCSR0C |= (1 << UPM00);
        } else if (config.parity != 0) {
        return 1;
    }
    sei();                     // turn on interrupts
    while(1);
    return 0;
}

// Transmits a single character
void USART_Transmit_char(uint8_t data)
{
    /* Wait for empty transmit buffer */
    while ( !( UCSR0A & (1<<UDRE0)) )
    ;
    /* Put data into buffer, sends the data */
    UDR0 = data;
}

/* Function to delete the entire linked list */
void byetx(struct node_tx** head_ref)
{
   /* deref head_ref to get the real head */
   struct node_tx* current = *head_ref;
   struct node_tx* next;

   while (current != NULL)
   {
       next = current->next;
       free(current);
       current = next;
   }

   /* deref head_ref to affect the real head back
      in the caller. */
   *head_ref = NULL;
}

/* Function to delete the entire linked list */
void byerx(struct node_rx ** head_ref)
{
   /* deref head_ref to get the real head */
   struct node_rx* current = *head_ref;
   struct node_rx* next;

   while (current != NULL)
   {
       next = current->next;
       free(current);
       current = next;
   }

   /* deref head_ref to affect the real head back
      in the caller. */
   *head_ref = NULL;
}

ISR (PCINT0_vect)
{
    USART_Transmit_char('r');
    /* interrupt code here */
    if ( (PINB & (1 << PINB0)) == 1 ) {
        portbhistory = 0;
        USART_Transmit_char('L');
        PORTB |= (1<<PORTB0);
    }
    else {
        portbhistory = 1;
        USART_Transmit_char('H');
        PORTB &= ~(1<<PORTB0);
    }
}


ISR(USART_RX_vect)
{
    //PORTB ^= (1 << PORTB5);
    volatile uint8_t u8temp;
    u8temp=UDR0;
    //USART_Transmit_char(u8temp);
    int value;

    pushrx(&headd, u8temp);
    if (u8temp == '\r'){
        //volatile node_tx * current = head;
        volatile struct node_rx * curr = headd;
        while ((curr) != NULL) { // set curr to head, stop if list empty.
            value = curr->val;
            pushtx(&head, value);
            curr = curr->next;          // advance head to next element.
            //free(curr->val);
        }
        byerx(headd);
        //disable reception and RX Complete interrupt
        UCSR0B &= ~((1<<RXEN0)|(1<<RXCIE0));
        //enable transmission and UDR0 empty interrupt
        UCSR0B |= (1<<TXEN0)|(1<<UDRIE0);
    }
}


ISR(USART_UDRE_vect)
{

    volatile char ch;

    volatile struct node_tx * current = head;
    while (current != NULL) {
        //printf("%d\n", current->val);
        ch = current->val; //pop(&head);
        USART_Transmit_char(ch);
        //free(current);
        //USART_Transmit_char('c');
        //USART_Transmit_char(current->val);
        current = current->next;
        //free(current);
    }
    if (ch == '\r'){
        byetx(&head);
        //enable reception and RX Complete interrupt
        UCSR0B |= ((1<<RXEN0)|(1<<RXCIE0));
        //disable transmission and UDR0 empty interrupt
        UCSR0B &= ~(1<<TXEN0)|(1<<UDRIE0);
    }
}
 

Papabravo

Joined Feb 24, 2006
21,158
Default assumption: there is absolutely nothing wrong with the hardware on the ATMega328.

My big 3 reasons for your code failure:
  1. Is there some reason why you feel it is OK to engage in busy waiting inside an interrupt routine?
  2. Do you have a clue about how much time you are spending inside the interrupt routines?
  3. Is there any chance the same variables are being manipulated inside an interrupt routine and outside of it?
Your code appears to be bloated and poorly structured. Your reason for moving the data twice is unclear. Maybe some spaghetti genius can figure out what you're doing and why.

#1 suggestion: Move most of your code OUTSIDE of the interrupt routine. Inside an interrupt is no place to do data processing or data movement of multiple characters.
#2 suggestion: How accurate is your baudrate generator with respect to the necessary baudrate. Come to think of it what is the baudrate and what is your crystal frequency?
 
Last edited:

Thread Starter

kadawn

Joined Oct 3, 2018
7
1) I don't think I'm busy waiting. The RX interrupt only checks for EOL with an if and the UDRE interrupt only activates once so I have to send each character separately for a string, and then erase the linked list.

2) Too much.

3) No, the interrupts can't happen at the same time because I make them take turns with enables and disables.

The spaghetti genius is the one that invented the requirements for this code.
 
Last edited:

Papabravo

Joined Feb 24, 2006
21,158
1) I don't think I'm busy waiting. The RX interrupt only checks for EOL with an if and the UDRE interrupt only activates once so I have to send each character separately for a string, and then erase the linked list.

2) Too much.

3) No, the interrupts can't happen at the same time because I make them take turns with enables and disables.

The spaghetti genius is the one that invented the requirements for this code.
There is more than one way to implement a set of requirements. You just happen to have chosen the worst possible one. You can take my suggestions at face value and dig deeper or you can react defensively and dismiss them out of hand because you believe that an attack on your code is an attack on your person. I assure you it is not. I am interested in guiding you in the direction of learning how to do things properly. I can't fix the code for you; you must discover the way to do that on your own.

Your answer to question number 2 is the key feature of your problem

I know the interrupts don't happen at the same time, that was not what I was saying. If you have TWO interrupt routines and they BOTH modify the same data, that can be a problem that will produce a deadlock.

My suggestions come from a career in embedded design that spans half a century, I think you might want to spend some time considering what I have said.
 

Thread Starter

kadawn

Joined Oct 3, 2018
7
There is more than one way to implement a set of requirements. You just happen to have chosen the worst possible one. You can take my suggestions at face value and dig deeper or you can react defensively and dismiss them out of hand because you believe that an attack on your code is an attack on your person. I assure you it is not. I am interested in guiding you in the direction of learning how to do things properly. I can't fix the code for you; you must discover the way to do that on your own.

Your answer to question number 2 is the key feature of your problem

I know the interrupts don't happen at the same time, that was not what I was saying. If you have TWO interrupt routines and they BOTH modify the same data, that can be a problem that will produce a deadlock.

My suggestions come from a career in embedded design that spans half a century, I think you might want to spend some time considering what I have said.
I'm sorry if you think I'm reacting defensively. Actually I was taking what you said into account and made a new code (looks worse than the other one but should take less time inside interrupts).
Code:
// includes
#define F_CPU    16000000UL        // MCU Clock Speed - needed for baud rate value computation
#define TX_BUFFLEN 30            // Send buffer size
#define RX_BUFFLEN 30            // Receive buffer size


// includes
#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdlib.h>

#include "USART_implement_me.h"

int i = 0;
int j = 0;
int CLEAR = 0;
int TRANS = 0;

volatile uint8_t portbhistory = 0;     // default is high because the pull-up
void deleteNode(struct node_tx **head_ref, int position) ;
void deleteNoderx(struct node_rx **head_ref, int position);


void pushtx(volatile struct node_tx ** head, int val) {
    /* now we can add a new variable */
    struct node_tx * new_node = (struct node_tx*)malloc(sizeof(struct node_tx));
    volatile struct node_tx *last = *head;  /* used in step 5*/

    /* 2. put in the data  */
    new_node->val  = val;

    /* 3. This new node is going to be the last node, so make next
          of it as NULL*/
    new_node->next = NULL;

    /* 4. If the Linked List is empty, then make the new node as head */
    if (*head == NULL)
    {
       *head = new_node;
       return;
    }
 
    /* 5. Else traverse till the last node */
    while (last->next != NULL) {
        last = last->next;
    }
    /* 6. Change the next of last node */
    last->next = new_node;
    return;
}

void pushrx(volatile struct node_rx ** head, int val) {
    /* now we can add a new variable */
    struct node_rx * new_node = (struct node_rx*)malloc(sizeof(struct node_rx));
    volatile struct node_rx *last = *head;  /* used in step 5*/

    /* 2. put in the data  */
    new_node->val  = val;

    /* 3. This new node is going to be the last node, so make next
          of it as NULL*/
    new_node->next = NULL;

    /* 4. If the Linked List is empty, then make the new node as head */
    if (*head == NULL)
    {
       *head = new_node;
       return;
    }
 
    /* 5. Else traverse till the last node */
    while (last->next != NULL) {
        last = last->next;
    }
    /* 6. Change the next of last node */
    last->next = new_node;
    USART_Transmit_char(new_node->val);
    return;
}



// The initialisation function. Call it once from your main() program before
// issuing any USART commands with the functions below!
//
// Call it at any time to change the USART communication parameters.
//
// Returns zero in case of success, non-zero for errors.
uint8_t USART_Init(struct USART_configuration config)
{
    DDRD = (1 << DDB5);

    // You can recycle your previous code. But remember, this time you are
    // supposed to configure interrupts! Maybe you]ll have to extend it a little bit?
    unsigned int ubrr = F_CPU/16/config.baudrate-1;
    /*Set baud rate */
    UBRR0H = (unsigned char)(ubrr>>8);
    UBRR0L = (unsigned char)ubrr;
    /*Enable receiver and transmitter */
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    /* Set frame format: 8data, 2stop bit */

    DDRB &= ~(1 << DDB0);     // Clear the PB0 pin
    // PB0 (PCINT0 pin) is now an input

    PORTB |= (1 << PORTB0);    // turn On the Pull-up
    // PB0 is now an input with pull-up enabled


    PCICR |= (1 << PCIE0);    // set PCIE0 to enable PCMSK0 scan
    PCMSK0 |= (1 << PCINT0);  // set PCINT0 to trigger an interrupt on state change

    UCSR0B |=
    /* interrupt on receive */
    (1 << TXCIE0);
    UCSR0B |=
    ///* interrupt on send */
    (1 << RXCIE0);
    //UCSR0B |=
    ///* interrupt on send */
    //(1 << UDRIE0);

    volatile struct node_rx * headd = NULL;
    headd = (struct node_rx*)malloc(sizeof(struct node_rx));
    if (headd == NULL) {
        USART_Transmit_char('e');
        return 1;
    }
    //headd->val = ' ';
    //headd->next = NULL;

    volatile struct node_tx * head = NULL;
    head = (struct node_tx*)malloc(sizeof(struct node_tx));
    if (head == NULL) {
        USART_Transmit_char('e');
        return 1;
    }
    //head->val = ' ';
    //head->next = NULL;

    if (config.databits == 8)
    {
        UCSR0C |= (1<<USBS0);
    }
    else if (config.databits == 7){
        UCSR0C |= (0<<USBS0);
    }
    else if (config.databits == 9){
        //Activates 9th data bit
        UCSR0C |= (1<<USBS0);
        UCSR0B |= (1 << UCSZ02);
    }
    else {
        return 1;
    }
    if (config.stopbits == 1){
        UCSR0C|= (1<<UCSZ00);
    }
    else if (config.stopbits == 2) {
        UCSR0C|= (3<<UCSZ00);
    }
    else {
        return 1;
    }
    if (config.parity == 1) {
        UCSR0C |= (1 << UPM01);
    }
    else if (config.parity == 2) {
        UCSR0C |= (1 << UPM01);
        UCSR0C |= (1 << UPM00);
        } else if (config.parity != 0) {
        return 1;
    }
    sei();                     // turn on interrupts
    int a;
    int b;
    int c;
    while(1){
        a = 0;
        b = i;
        c = j;
        char value;
        if (CLEAR == 1){
            for( a = 0; a <= i; a = a + 1 ){
                deleteNode(&head, a);
            }
            i = i - b;
            CLEAR = 0;
        }
        a = 0;
        if ((TRANS == 1)&&(CLEAR == 0)){
            //volatile node_tx * current = head;
            volatile struct node_rx * curr = headd;
            while ((curr) != NULL) { // set curr to head, stop if list empty.
                //USART_Transmit_char('o');
                value = curr->val;
                pushtx(&head, value);
                curr = curr->next;          // advance head to next element.
                //free(curr->val);
            }
            for( a = 0; a <= j; a = a + 1 ){
                deleteNoderx(&headd, a);
            }
            j = j - c;
            TRANS = 0;
            USART_Transmit_char(' ');
            //UDR0 = ' ';
        }
    };
    return 0;
}

// Transmits a single character
void USART_Transmit_char(uint8_t data)
{
    /* Wait for empty transmit buffer */
    while ( !( UCSR0A & (1<<UDRE0)) )
    ;
    /* Put data into buffer, sends the data */
    UDR0 = data;
}

/* Function to delete the entire linked list */
void byetx(struct node_tx** head_ref)
{
   /* deref head_ref to get the real head */
   struct node_tx* current = *head_ref;
   struct node_tx* next;

   while (current != NULL)
   {
       next = current->next;
       free(current);
       current = next;
   }

   /* deref head_ref to affect the real head back
      in the caller. */
   *head_ref = NULL;
}

/* Function to delete the entire linked list */
void byerx(struct node_rx ** head_ref)
{
   /* deref head_ref to get the real head */
   struct node_rx* current = *head_ref;
   struct node_rx* next;

   while (current != NULL)
   {
       next = current->next;
       free(current);
       current = next;
   }

   /* deref head_ref to affect the real head back
      in the caller. */
   *head_ref = NULL;
}

/* Given a reference (pointer to pointer) to the head of a list
   and a position, deletes the node at the given position */
void deleteNode(struct node_tx **head_ref, int position)
{
   // If linked list is empty
   if (*head_ref == NULL)
      return;

   // Store head node
   struct node_tx* temp = *head_ref;

    // If head needs to be removed
    if (position == 0)
    {
        *head_ref = temp->next;   // Change head
        free(temp);               // free old head
        return;
    }

    // Find previous node of the node to be deleted
    for (int i=0; temp!=NULL && i<position-1; i++)
         temp = temp->next;

    // If position is more than number of ndoes
    if (temp == NULL || temp->next == NULL)
         return;

    // Node temp->next is the node to be deleted
    // Store pointer to the next of node to be deleted
    struct node_tx *next = temp->next->next;

    // Unlink the node from linked list
    free(temp->next);  // Free memory

    temp->next = next;  // Unlink the deleted node from list
}

void deleteNoderx(struct node_rx **head_ref, int position)
{
    // If linked list is empty
    if (*head_ref == NULL)
    return;

    // Store head node
    struct node_rx* temp = *head_ref;

    // If head needs to be removed
    if (position == 0)
    {
        *head_ref = temp->next;   // Change head
        free(temp);               // free old head
        return;
    }

    // Find previous node of the node to be deleted
    for (int i=0; temp!=NULL && i<position-1; i++)
    temp = temp->next;

    // If position is more than number of ndoes
    if (temp == NULL || temp->next == NULL)
    return;

    // Node temp->next is the node to be deleted
    // Store pointer to the next of node to be deleted
    struct node_rx *next = temp->next->next;

    // Unlink the node from linked list
    free(temp->next);  // Free memory

    temp->next = next;  // Unlink the deleted node from list
}

ISR (PCINT0_vect)
{
    USART_Transmit_char('r');
    /* interrupt code here */
    if ( (PINB & (1 << PINB0)) == 1 ) {
        portbhistory = 0;
        USART_Transmit_char('L');
        PORTB |= (1<<PORTB0);
    }
    else {
        portbhistory = 1;
        USART_Transmit_char('H');
        PORTB &= ~(1<<PORTB0);
    }
}


ISR(USART_RX_vect)
{
    //PORTB ^= (1 << PORTB5);
    volatile uint8_t u8temp;
    u8temp=UDR0;
    j += 1;
    //USART_Transmit_char(u8temp);

    pushrx(&headd, u8temp);
    if (u8temp == '\r'){
        TRANS = 1;
        //disable reception and RX Complete interrupt
        //UCSR0B &= ~((1<<RXEN0)|(1<<RXCIE0));
        //enable transmission and UDR0 empty interrupt
        //UCSR0B |= (1<<TXEN0)|(1<<UDRIE0);
    }
}

ISR(USART_TX_vect){
    volatile struct node_tx * current = head;
    if (current != NULL){
        i += 1;
        USART_Transmit_char(current->val);
        if (current->val == '\r'){
            CLEAR = 1;
            //byetx(&head);
            //disable transmission and UDR0 empty interrupt
            //UCSR0B &= ~(1<<TXEN0)|(1<<UDRIE0);
        }
    }
}
Now I created a new problem and that is the new characters in the RX interrupt don't get into the linked list. This is before any deadlock can happen. About the projected normal execution of this code, there should be no problems with interrupts using the same buffer at the same time and the memory freeing is done element by element to not stop sending or receiving.
 
Last edited:

Papabravo

Joined Feb 24, 2006
21,158
You still haven't mentioned the baudrate you are using. This may affect the ability to achieve reliable operation with a 16 MHz. crystal.
 

Thread Starter

kadawn

Joined Oct 3, 2018
7
I'm using 57600 and I can't change that.
About the receive problem, I pinpointed that the characters are getting added into the linked list but that change isn't reflected elsewhere.
 

Thread Starter

kadawn

Joined Oct 3, 2018
7
Ok now it works, it was a main local variable.
Now, when I send a very large paragraph the message gets cut on send. It works fine for long lines.
Code:
// includes
#define F_CPU    16000000UL        // MCU Clock Speed - needed for baud rate value computation
#define TX_BUFFLEN 30            // Send buffer size
#define RX_BUFFLEN 30            // Receive buffer size


// includes
#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdlib.h>

#include "USART_implement_me.h"

int CLEAR = 0;
int TRANS = 0;

volatile uint8_t portbhistory = 0;     // default is high because the pull-up
void deleteNode(struct node_tx **head_ref, int position) ;
void deleteNoderx(struct node_rx **head_ref, int position);
void byetx(struct node_tx** head_ref) ;
void byerx(struct node_rx ** head_ref) ;


// This function prints contents of linked list starting from
// the given node
void printList(volatile struct node_rx *node)
{
    while (node != NULL)
    {
        USART_Transmit_char(node->val);
        node = node->next;
    }
}

/* Counts no. of nodes in linked list */
int getCount(volatile struct node_rx * head)
{
    int count = 0;  // Initialize count
    volatile struct node_rx * current = head;  // Initialize current
    while (current != NULL)
    {
        count++;
        current = current->next;
    }
    return count;
}
/* Counts no. of nodes in linked list */
int getCounttx(volatile struct node_tx * head)
{
    int count = 0;  // Initialize count
    volatile struct node_tx * current = head;  // Initialize current
    while (current != NULL)
    {
        count++;
        current = current->next;
    }
    return count;
}

void pushtx(volatile struct node_tx ** head, int val) {
    /* now we can add a new variable */
    struct node_tx * new_node = (struct node_tx*)malloc(sizeof(struct node_tx));
    volatile struct node_tx *last = *head;  /* used in step 5*/
  
    /* 2. put in the data  */
    new_node->val  = val;
 
    /* 3. This new node is going to be the last node, so make next 
          of it as NULL*/
    new_node->next = NULL;
 
    /* 4. If the Linked List is empty, then make the new node as head */
    if (*head == NULL)
    {
       *head = new_node;
       return;
    }  
      
    /* 5. Else traverse till the last node */
    while (last->next != NULL) {
        last = last->next;
    }
    /* 6. Change the next of last node */
    last->next = new_node;
    return;
}

void pushrx(volatile struct node_rx ** head, int val) {
    /* now we can add a new variable */
    struct node_rx * new_node = (struct node_rx*)malloc(sizeof(struct node_rx));
    volatile struct node_rx *last = *head;  /* used in step 5*/
  
    /* 2. put in the data  */
    new_node->val  = val;
 
    /* 3. This new node is going to be the last node, so make next 
          of it as NULL*/
    new_node->next = NULL;
   
    //USART_Transmit_char(new_node->val);
 
    /* 4. If the Linked List is empty, then make the new node as head */
    if (*head == NULL)
    {
       *head = new_node;
       //USART_Transmit_char('e');
       return;
    }  
      
    /* 5. Else traverse till the last node */
    while (last->next != NULL) {
        last = last->next;
    }
    /* 6. Change the next of last node */
    last->next = new_node;
    return;
}

void mainee(void){
    char value;
    if (CLEAR == 1){
        //for( a = 0; a <= i; a = a + 1 ){
        deleteNode(&head, 0);
        //}
        CLEAR = 0;
    }
    int i = 0;
    if ((TRANS == 1)&&(CLEAR == 0)){
        //printList(headd);
        //char character = getCount(headd) + '0';
        //USART_Transmit_char(character);
        //volatile node_tx * current = head;
        volatile struct node_rx * curr = headd;
        while ((curr) != NULL) { // set curr to head, stop if list empty.
            //USART_Transmit_char('o');
            value = curr->val;
            pushtx(&head, value);
            curr = curr->next;          // advance head to next element.
            i++;
            //free(curr->val);
        }
        for(int a = 0; a <= i; a++ ){
            //char character = getCount(headd) + '0';
            //USART_Transmit_char(character);
            deleteNoderx(&headd, 0);
        }
        TRANS = 0;
        USART_Transmit_char(' ');
        //UDR0 = ' ';
    }
}

// The initialisation function. Call it once from your main() program before
// issuing any USART commands with the functions below!
//
// Call it at any time to change the USART communication parameters.
//
// Returns zero in case of success, non-zero for errors.
uint8_t USART_Init(struct USART_configuration config)
{
    DDRD = (1 << DDB5);
   
    // You can recycle your previous code. But remember, this time you are
    // supposed to configure interrupts! Maybe you]ll have to extend it a little bit?
    unsigned int ubrr = F_CPU/16/config.baudrate-1;
    /*Set baud rate */
    UBRR0H = (unsigned char)(ubrr>>8);
    UBRR0L = (unsigned char)ubrr;
    /*Enable receiver and transmitter */
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    /* Set frame format: 8data, 2stop bit */
   
    DDRB &= ~(1 << DDB0);     // Clear the PB0 pin
    // PB0 (PCINT0 pin) is now an input

    PORTB |= (1 << PORTB0);    // turn On the Pull-up
    // PB0 is now an input with pull-up enabled


    PCICR |= (1 << PCIE0);    // set PCIE0 to enable PCMSK0 scan
    PCMSK0 |= (1 << PCINT0);  // set PCINT0 to trigger an interrupt on state change
   
    UCSR0B |=
    /* interrupt on receive */
    (1 << TXCIE0);
    UCSR0B |=
    ///* interrupt on send */
    (1 << RXCIE0);
    //UCSR0B |=
    ///* interrupt on send */
    //(1 << UDRIE0);
   
    volatile struct node_rx * headd = NULL;
    headd = (struct node_rx*)malloc(sizeof(struct node_rx));
    if (headd == NULL) {
        USART_Transmit_char('e');
        return 1;
    }
    //headd->val = ' ';
    //headd->next = NULL;
   
    volatile struct node_tx * head = NULL;
    head = (struct node_tx*)malloc(sizeof(struct node_tx));
    if (head == NULL) {
        USART_Transmit_char('e');
        return 1;
    }
    //head->val = ' ';
    //head->next = NULL;
   
    if (config.databits == 8)
    {
        UCSR0C |= (1<<USBS0);
    }
    else if (config.databits == 7){
        UCSR0C |= (0<<USBS0);
    }
    else if (config.databits == 9){
        //Activates 9th data bit
        UCSR0C |= (1<<USBS0);
        UCSR0B |= (1 << UCSZ02);
    }
    else {
        return 1;
    }
    if (config.stopbits == 1){
        UCSR0C|= (1<<UCSZ00);
    }
    else if (config.stopbits == 2) {
        UCSR0C|= (3<<UCSZ00);
    }
    else {
        return 1;
    }
    if (config.parity == 1) {
        UCSR0C |= (1 << UPM01);
    }
    else if (config.parity == 2) {
        UCSR0C |= (1 << UPM01);
        UCSR0C |= (1 << UPM00);
        } else if (config.parity != 0) {
        return 1;
    }
    //byerx(headd);
    //byetx(head);
    sei();                     // turn on interrupts
    while(1){
        mainee();
    };
    return 0;
}

// Transmits a single character
void USART_Transmit_char(uint8_t data)
{
    /* Wait for empty transmit buffer */
    while ( !( UCSR0A & (1<<UDRE0)) )
    ;
    /* Put data into buffer, sends the data */
    UDR0 = data;
}

/* Function to delete the entire linked list */
void byetx(struct node_tx** head_ref)
{
   /* deref head_ref to get the real head */
   struct node_tx* current = *head_ref;
   struct node_tx* next;
 
   while (current != NULL) 
   {
       next = current->next;
       free(current);
       current = next;
   }
   
   /* deref head_ref to affect the real head back
      in the caller. */
   *head_ref = NULL;
}

/* Function to delete the entire linked list */
void byerx(struct node_rx ** head_ref)
{
   /* deref head_ref to get the real head */
   struct node_rx* current = *head_ref;
   struct node_rx* next;
 
   while (current != NULL) 
   {
       next = current->next;
       free(current);
       current = next;
   }
   
   /* deref head_ref to affect the real head back
      in the caller. */
   *head_ref = NULL;
}

/* Given a reference (pointer to pointer) to the head of a list
   and a position, deletes the node at the given position */
void deleteNode(struct node_tx **head_ref, int position)
{
   // If linked list is empty
   if (*head_ref == NULL)
      return;
 
   // Store head node
   struct node_tx* temp = *head_ref;
 
    // If head needs to be removed
    if (position == 0)
    {
        *head_ref = temp->next;   // Change head
        free(temp);               // free old head
        return;
    }
 
    // Find previous node of the node to be deleted
    for (int o=0; temp!=NULL && o<position-1; o++)
         temp = temp->next;
 
    // If position is more than number of ndoes
    if (temp == NULL || temp->next == NULL)
         return;
 
    // Node temp->next is the node to be deleted
    // Store pointer to the next of node to be deleted
    struct node_tx *next = temp->next->next;
 
    // Unlink the node from linked list
    free(temp->next);  // Free memory
 
    temp->next = next;  // Unlink the deleted node from list
}

void deleteNoderx(struct node_rx **head_ref, int position)
{
    // If linked list is empty
    if (*head_ref == NULL)
    return;
   
    // Store head node
    struct node_rx* temp = *head_ref;
   
    // If head needs to be removed
    if (position == 0)
    {
        *head_ref = temp->next;   // Change head
        free(temp);               // free old head
        return;
    }
   
    // Find previous node of the node to be deleted
    for (int p=0; temp!=NULL && p<position-1; p++)
    temp = temp->next;
   
    // If position is more than number of ndoes
    if (temp == NULL || temp->next == NULL)
    return;
   
    // Node temp->next is the node to be deleted
    // Store pointer to the next of node to be deleted
    struct node_rx *next = temp->next->next;
   
    // Unlink the node from linked list
    free(temp->next);  // Free memory
   
    temp->next = next;  // Unlink the deleted node from list
}

ISR (PCINT0_vect)
{
    USART_Transmit_char('r');
    /* interrupt code here */
    if ( (PINB & (1 << PINB0)) == 1 ) {
        portbhistory = 0;
        USART_Transmit_char('L');
        PORTB |= (1<<PORTB0);
    }
    else {
        portbhistory = 1;
        USART_Transmit_char('H');
        PORTB &= ~(1<<PORTB0);
    }
}


ISR(USART_RX_vect)
{
    //PORTB ^= (1 << PORTB5);
    volatile uint8_t u8temp;
    u8temp=UDR0;
    //USART_Transmit_char(u8temp);
   
    pushrx(&headd, u8temp);
    if (u8temp == '\r'){
        TRANS = 1;
        //disable reception and RX Complete interrupt
        //UCSR0B &= ~((1<<RXEN0)|(1<<RXCIE0));
        //enable transmission and UDR0 empty interrupt
        //UCSR0B |= (1<<TXEN0)|(1<<UDRIE0);
    }
}

ISR(USART_TX_vect){
    volatile struct node_tx * current = head;
    if ((current != NULL)&&(TRANS == 0)&&(CLEAR == 0)){
        //char character = getCounttx(head) + '0';
        //USART_Transmit_char(character);
        USART_Transmit_char(current->val);
        CLEAR = 1;
            //byetx(&head);
            //disable transmission and UDR0 empty interrupt
            //UCSR0B &= ~(1<<TXEN0)|(1<<UDRIE0);
    }
}
 

Thread Starter

kadawn

Joined Oct 3, 2018
7
You still haven't mentioned the baudrate you are using. This may affect the ability to achieve reliable operation with a 16 MHz. crystal.
Forgot to reply. I still can´t find out why the longest strings get cut.
I have to make it work wit very large strings with no time limit.
 
Top