piątek, 24 lutego 2017

[6] Atmega328p - USART

Ten post poświęcę na opisanie sposobu komunikacja za pośrednictwem USART-u w mikrokontrolerze ATmega328p. Zostaną przedstawione zarówno funkcje wysyłające jak i odbierające dane. Po drobnych przeróbkach powinna ona działać z większość procesorów z tej rodziny.



Do testów wykorzystam płytkę Arduino nano. Piny PD0 (RX) oraz PD1(TX) zostały podłączone do wbudowanego konwertera UART - USB podpiętego do złącza mini USB.

Poniżej przedstawię wszystkie potrzebne funkcje wymagane do wykonania komunikacji z układem:

Na samym początku wprowadzam definicje potrzebnych bibliotek, następnie długość bufora oraz rodzaj zegara. Dalej zdefiniowane są rodzaje portu usartu (ten mikrokontroler posiada tylko jeden, ale w przypadku rozbudowy pod inną atmegę można dodać do typu wyliczeniowego).

  1. #include <avr/interrupt.h>
  2. #include <stdbool.h>
  3. //=================================================================
  4. #define TX_BUFFER_LEN 200 
  5. #define RX_BUFFER_LEN 200           //Dlugosc bufora odebranych danych
  6. #define CLOCK_TYPE  1               //Rodzaj zegara
  7. //=================================================================
  8. typedef enum {
  9.   USART_PORT_0 = 0,                 //Numer USART'a, ten mikrokontroler posiada jeden
  10. }USART_PORT;
  11. //=================================================================
  12. typedef enum {                      //Rodzaj zegara
  13.     Clock_16Mhz = 0,
  14.     Clock_8Mhz = 1,
  15.     Clock_1Mhz = 2
  16. }CLOCK_TYPE_e;
  17. //=================================================================
  18. typedef enum {
  19.   BaudRate_4800 = 0,                //Baudrate
  20.   BaudRate_9600 = 1,
  21.   BaudRate_14400 = 2,
  22.   BaudRate_19200 = 3,
  23.   BaudRate_28800 = 4,
  24.   BaudRate_38400 = 5,
  25.   BaudRate_57600 = 6,
  26.   BaudRate_115200 = 7,
  27.   BaudRate_230400 = 8
  28. }BAUDRATE;
  29. //=================================================================
  30. typedef enum {                      //Znak zakonczenia wiadomosci
  31.   END_NOP_ASCII = 0,                //znak pusty
  32.   END_CR_ASCII = 1,                 //znak 0x0D
  33.   END_LF_ASCII = 2,                 //znak 0x0A
  34.   END_CRLF_ASCII = 3                //znak 0x0D, 0x0A
  35. }RX_END_ASCII;
  36. //=================================================================

Poniżej znajduje się funkcja inicjalizująca USART. W pierwszej kolejności zostaje wybrana prędkość na podstawie częstotliwości zegara

  1. void USART_INIT(USART_PORT port, BAUDRATE baud)
  2. {
  3.   uint8_t presc=0;
  4.  
  5.   if(CLOCK_TYPE == 0)       // 16MHz
  6.   {
  7.     if(baud==BaudRate_4800)         {   presc=207;  }   //0.2%
  8.     else if(baud==BaudRate_9600)    {   presc=103;  }   //0.2%
  9.     else if(baud==BaudRate_14400)   {   presc=68;   }   //0.6%
  10.     else if(baud==BaudRate_19200)   {   presc=51;   }   //0.2%
  11.     else if(baud==BaudRate_28800)   {   presc=34;   }   //-0.8%
  12.     else if(baud==BaudRate_38400)   {   presc=25;   }   //0.2%
  13.     else if(baud==BaudRate_57600)   {   presc=16;   }   //2.1%
  14.     else if(baud==BaudRate_115200)  {   presc=8;    }   //-3.5%
  15.     else if(baud==BaudRate_230400)  {   presc=3;    }   //8.5%
  16.   }
  17.   else if(CLOCK_TYPE == 1)  //8MHz
  18.   {
  19.     if(baud==BaudRate_4800)         {   presc=103;  }   //0.2%
  20.     else if(baud==BaudRate_9600)    {   presc=51;   }   //0.2%
  21.     else if(baud==BaudRate_14400)   {   presc=34;   }   //-0.8%
  22.     else if(baud==BaudRate_19200)   {   presc=25;   }   //0.2%
  23.     else if(baud==BaudRate_28800)   {   presc=16;   }   //2.1%
  24.     else if(baud==BaudRate_38400)   {   presc=12;   }   //0.2%
  25.     else if(baud==BaudRate_57600)   {   presc=8;    }   //-7%
  26.     else if(baud==BaudRate_115200)  {   presc=3;    }   //8.5%
  27.     else if(baud==BaudRate_230400)  {   presc=1;    }   //0%
  28.   }
  29.   else if(CLOCK_TYPE == 2) //1MHz
  30.   {
  31.     if(baud==BaudRate_4800)         {   presc=12;   }   //0.2%
  32.     else if(baud==BaudRate_9600)    {   presc=6;    }   //-7%
  33.     else if(baud==BaudRate_14400)   {   presc=3;    }   //8.5%
  34.     else if(baud==BaudRate_19200)   {   presc=2;    }   //8.5%
  35.   }
  36.  
  37.   if(port==USART_PORT_0)
  38.   {
  39.     //Ustawienie baudrate
  40.     UBRR0H=0x00;
  41.     UBRR0L=presc;
  42.     //---------------------------
  43.     UCSR0A=0x00;
  44.     //---------------------------
  45.     UCSR0B=0x00;
  46.     UCSR0B|=0x80 | 0x10 | 0x08;     //Rx przerwania on, rx enable, tx enalbe
  47.     //---------------------------
  48.     UCSR0C=0x00;                    // tryb asynchroniczny, bez przystosci, 1 bit stopu
  49.     UCSR0C|=0x04 | 0x02;            // 8 date bits
  50.     //---------------------------
  51.   }
  52.   sei();
  53. }

Przesłanie pojedynczego znaku:

  1. void Stat_Send_Char(USART_PORT port_number, char data)
  2. {    
  3.     if(port_number==USART_PORT_0)
  4.     {
  5.       while(!(UCSR0A & 0x20));
  6.       UDR0=data;
  7.     }
  8. }

Przesłanie ciągu znaków:

  1. void USART_SEND(USART_PORT port_number, const char *buf, RX_END_ASCII end_sign)
  2. {
  3.   uint8_t n;
  4.   uint8_t c;
  5.  
  6.   for(n=0;n<=TX_BUFFER_LEN;n++)
  7.   {
  8.     c=buf[n];
  9.    
  10.     if(c==0)                                //Jesli natrafi na koniec bufora
  11.     {
  12.       if(end_sign==END_CR_ASCII)
  13.       {
  14.         Stat_Send_Char(port_number, CHAR_CR);
  15.       }
  16.       else if(end_sign==END_LF_ASCII)
  17.       {
  18.         Stat_Send_Char(port_number, CHAR_LF);  
  19.       }
  20.       else if(end_sign==END_CRLF_ASCII)
  21.       {
  22.         Stat_Send_Char(port_number, CHAR_CR);    
  23.         Stat_Send_Char(port_number, CHAR_LF);      
  24.       }
  25.       break;
  26.     }
  27.     else
  28.     {
  29.       Stat_Send_Char(port_number, c);
  30.     }  
  31.   }
  32. }

Dane wysyłane są aż do natrafienie na zakończenie bufora. Po nim dodawany jest znak kończący.

Odczytanie danych wykonywane jest w przerwaniu. Następuje przejście po każdym znaku aż do maksymalnej wielkości bufora.

  1. ISR(USART_RX_vect)
  2. {    
  3.   uint8_t recive_char;
  4.  
  5.   recive_char=UDR0;
  6.  
  7.   if(recive_char==CHAR_CR) { stat_ok=true; }
  8.   else
  9.   {
  10.     if(stat_ok==false)
  11.      {
  12.       if(position_0<RX_BUFFER_LEN)
  13.       {
  14.         if((recive_char>=ASCII_MIN) && (recive_char<=ASCII_MAX))
  15.         {
  16.           buffer_0[position_0]=recive_char;
  17.           buffer_0[position_0+1]=0x00;
  18.           position_0++;
  19.         }
  20.       }
  21.     }
  22.   }  
  23.   Stat_Send_Char(USART_PORT_0, recive_char);
  24. }

Cały kod zebrany w pliku biblioteki:

  1. #include <avr/interrupt.h>
  2. #include <stdbool.h>
  3. //=================================================================
  4. #define TX_BUFFER_LEN 200
  5. #define RX_BUFFER_LEN 200           //Dlugosc bufora odebranych danych
  6. #define CLOCK_TYPE  1               //Rodzaj zegara
  7. //=================================================================
  8. typedef enum {
  9.   USART_PORT_0 = 0,                         //Numer USART'a, ten mikrokontroler posiada jednen
  10. }USART_PORT;
  11. //=================================================================
  12. typedef enum {
  13.     Clock_16Mhz = 0,
  14.     Clock_8Mhz = 1,
  15.     Clock_1Mhz = 2
  16. }CLOCK_TYPE_e;
  17. //=================================================================
  18. typedef enum {
  19.   BaudRate_4800 = 0,
  20.   BaudRate_9600 = 1,
  21.   BaudRate_14400 = 2,
  22.   BaudRate_19200 = 3,
  23.   BaudRate_28800 = 4,
  24.   BaudRate_38400 = 5,
  25.   BaudRate_57600 = 6,
  26.   BaudRate_115200 = 7,
  27.   BaudRate_230400 = 8
  28. }BAUDRATE;
  29. //=================================================================
  30. typedef enum {
  31.   END_NOP_ASCII = 0,                //znak konczoncy pusty
  32.   END_CR_ASCII = 1,                 //znak konczoncy 0x0D
  33.   END_LF_ASCII = 2,                 //znak konczoncy 0x0A
  34.   END_CRLF_ASCII = 3                //znak konczoncy 0x0D, 0x0A
  35. }RX_END_ASCII;
  36. //=================================================================
  37. void USART_INIT(USART_PORT port, BAUDRATE baud);                    //Inicjalizacja
  38. void USART_SEND(USART_PORT port, const char *ptr, RX_END_ASCII ende);   //wyslanie danych
  39. //=================================================================
  40. //Funckje wewnetrzne
  41. static void Stat_Send_Char(USART_PORT port, char wert);    
  42. //=================================================================
  43. #define CHAR_CR     0x0d   
  44. #define CHAR_LF     0x0a   
  45. #define ASCII_MIN   0x20        //Min spacja
  46. #define ASCII_MAX   0x7e        //Max tylda
  47.  
  48. uint8_t buffer_0[RX_BUFFER_LEN];
  49. uint8_t position_0 = 0;
  50. volatile bool stat_ok = false;
  51. //=================================================================
  52. void USART_INIT(USART_PORT port, BAUDRATE baud)
  53. {
  54.   uint8_t presc=0;
  55.  
  56.   if(CLOCK_TYPE == 0)       // 16MHz
  57.   {
  58.     if(baud==BaudRate_4800)         {   presc=207;  }   //0.2%
  59.     else if(baud==BaudRate_9600)    {   presc=103;  }   //0.2%
  60.     else if(baud==BaudRate_14400)   {   presc=68;   }   //0.6%
  61.     else if(baud==BaudRate_19200)   {   presc=51;   }   //0.2%
  62.     else if(baud==BaudRate_28800)   {   presc=34;   }   //-0.8%
  63.     else if(baud==BaudRate_38400)   {   presc=25;   }   //0.2%
  64.     else if(baud==BaudRate_57600)   {   presc=16;   }   //2.1%
  65.     else if(baud==BaudRate_115200)  {   presc=8;    }   //-3.5%
  66.     else if(baud==BaudRate_230400)  {   presc=3;    }   //8.5%
  67.   }
  68.   else if(CLOCK_TYPE == 1)  //8MHz
  69.   {
  70.     if(baud==BaudRate_4800)         {   presc=103;  }   //0.2%
  71.     else if(baud==BaudRate_9600)    {   presc=51;   }   //0.2%
  72.     else if(baud==BaudRate_14400)   {   presc=34;   }   //-0.8%
  73.     else if(baud==BaudRate_19200)   {   presc=25;   }   //0.2%
  74.     else if(baud==BaudRate_28800)   {   presc=16;   }   //2.1%
  75.     else if(baud==BaudRate_38400)   {   presc=12;   }   //0.2%
  76.     else if(baud==BaudRate_57600)   {   presc=8;    }   //-7%
  77.     else if(baud==BaudRate_115200)  {   presc=3;    }   //8.5%
  78.     else if(baud==BaudRate_230400)  {   presc=1;    }   //0%
  79.   }
  80.   else if(CLOCK_TYPE == 2) //1MHz
  81.   {
  82.     if(baud==BaudRate_4800)         {   presc=12;   }   //0.2%
  83.     else if(baud==BaudRate_9600)    {   presc=6;    }   //-7%
  84.     else if(baud==BaudRate_14400)   {   presc=3;    }   //8.5%
  85.     else if(baud==BaudRate_19200)   {   presc=2;    }   //8.5%
  86.   }
  87.  
  88.   if(port==USART_PORT_0)
  89.   {
  90.     //Ustawienie baudrate
  91.     UBRR0H=0x00;
  92.     UBRR0L=presc;
  93.     //---------------------------
  94.     UCSR0A=0x00;
  95.     //---------------------------
  96.     UCSR0B=0x00;
  97.     UCSR0B|=0x80 | 0x10 | 0x08;     //Rx przerwania on, rx enable, tx enalbe
  98.     //---------------------------
  99.     UCSR0C=0x00;                    // tryb asynchroniczny, bez przystosci, 1 bit stopu
  100.     UCSR0C|=0x04 | 0x02;            // 8 date bits
  101.     //---------------------------
  102.   }
  103.   sei();
  104. }
  105. //=================================================================
  106. void USART_SEND(USART_PORT port_number, const char *buf, RX_END_ASCII end_sign)
  107. {
  108.   uint8_t n;
  109.   uint8_t c;
  110.  
  111.   for(n=0;n<=TX_BUFFER_LEN;n++)
  112.   {
  113.     c=buf[n];
  114.    
  115.     if(c==0)                                //Jesli natrafi na koniec bufora
  116.     {
  117.       if(end_sign==END_CR_ASCII)
  118.       {
  119.         Stat_Send_Char(port_number, CHAR_CR);
  120.       }
  121.       else if(end_sign==END_LF_ASCII)
  122.       {
  123.         Stat_Send_Char(port_number, CHAR_LF);  
  124.       }
  125.       else if(end_sign==END_CRLF_ASCII)
  126.       {
  127.         Stat_Send_Char(port_number, CHAR_CR);    
  128.         Stat_Send_Char(port_number, CHAR_LF);      
  129.       }
  130.       break;
  131.     }
  132.     else
  133.     {
  134.       Stat_Send_Char(port_number, c);
  135.     }  
  136.   }
  137. }
  138. //=================================================================
  139. void Stat_Send_Char(USART_PORT port, char data)
  140. {
  141.     if(port==USART_PORT_0)
  142.     {
  143.         while(!(UCSR0A & 0x20));
  144.         UDR0=data;
  145.     }
  146. }
  147. //=================================================================
  148. ISR(USART_RX_vect)
  149. {    
  150.   uint8_t recive_char;
  151.  
  152.   recive_char=UDR0;
  153.  
  154.   if(recive_char==CHAR_CR) { stat_ok=true; }
  155.   else
  156.   {
  157.     if(stat_ok==false)
  158.      {
  159.       if(position_0<RX_BUFFER_LEN)
  160.       {
  161.         if((recive_char>=ASCII_MIN) && (recive_char<=ASCII_MAX))
  162.         {
  163.           buffer_0[position_0]=recive_char;
  164.           buffer_0[position_0+1]=0x00;
  165.           position_0++;
  166.         }
  167.       }
  168.     }
  169.   }  
  170.   Stat_Send_Char(USART_PORT_0, recive_char);
  171. }