środa, 28 października 2015

[3a] Wysłanie danych z komputera - USART

Tym razem opiszę sposób przesłania danych z komputera poprzez port USART do mikrokontrolera.

Wstęp


Aby to wykonać taką transmisję należy odpowiednio zainicjalizować przerwania oraz jego obsługę dla wybranego USART-u. Należy ustawić wektor przerwać NVIC (ang. Nested Vector Interrupt Controller). Dzięki nim możliwe jest zatrzymanie działania programu, aby został wykonany inny fragment kodu zdefiniowany w procedurze obsługi przerwania.

Opisywany tutaj wektor przerwań pozwala na dynamiczną obsługę oraz wybranie ważniejszego przerwania. Możliwa jest obsługa do 256 różnych wywoływanych wektorów. Całość opisana jest w bibliotece misc.h. Nie będę się tutaj rozpisywał dokładnie nad jego zasadą działania, ponieważ zostanie to opisane w innym poście. 


Inicjalizacja USART-u oraz przerwania


Podstawową rzeczą jest zainicjalizowanie USART-u. Przeprowadza się to w sposób opisany prze zemnie w poście [4]. Natomiast żeby poprawnie obsługiwać dane przesłane z komputera na mikrokontroler należy dodatkowo zaprogramować przerwania NVIC. 

{
  NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
 
  //Wlaczenie przerwania dla RX
         USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
 
         //Włączenie obsługi IRQ
  NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  //Ustawienie priorytetu grupowego dla USART1 przypisane jest 0
  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
         //Podpriorytet
  NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
  NVIC_Init(&NVIC_InitStruct);
}

Drugim elementem jaki należy ustawić po inicjalizacji przerwania jest jego obsługa. Całość zawieramy w strukturze #ifdef...#endif. Kod ten zostanie wykonany gdy zostanie zgłoszone, że są przesyłane dane z komputera.

#ifdef USART1
void USART1_IRQHandler(void)
{
 //Sprawdzenie czy wystapilo przerwanie z powodu odebrania danych
 if (USART_GetITStatus(USART1, USART_IT_RXNE))
 {
  #ifdef USART1_USE_CUSTOM_IRQ
   //Wywołaj funckje przerwania
   USART1_ReceiveHandler(USART1->DR);
  #else
   //Podaj dane do Buffera
   USART_InsertToBuffer(0, USART1->DR);
  #endif
 }
}
#endif

Przerwanie może wystąpić z kilku powodów, wśród nich można wyróżnić następujące rodzaje:

  • USART_IT_CTS - zmiana na linii CTS , zmiana stanu na odbiorniku. Oznacza to, że może bądź nie może nadawać następnej informacji,
  • USART_IT_TXE - pusty rejestr nadawczy. Można nadawać kolejne dane.
  • USART_IT_RXNE - czyli to zdarzenie stosowane w tym przykładzie. Oznacza ono, że jest pusty rejestr odbiorczy, odebrano daną.
  • USART_IT_CT - dane wysłane, transmisja zakończona. Często wykorzystywane wraz z DMA.
Drugim rodzajem przerwań są te dotyczące błędów transmisji:

  • USART_IT_ORE - (ang. Overrun error) za dużo danych. Układ nie zdążył odczytać danych za nim zostały przesłane kolejne,
  • USART_IT_FE - (ang. Framing error) odebrano nieprawidłową ramkę danych,
  • USART_IT_NE - (ang. Noise error) zniekształcone dane,
  • USART_IT_PE - (ang. Parity error) błąd parzystości. Wystąpi gdy jest włączona kontrola parzystości i dane jej nie spełniły.

Inicjalizacja Pinów


Inicjalizacje pinów GPIO przeprowadza się w taki sam sposób co poprzednio, natomiast w tym przypadku należy oba piny ustawić jako wyjścia alternatywne.

void GPIO_Initialize(void)
{
  //konfigurowanie portow GPIO
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 
  //TX dla pinu PA9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
 
  //RX dla pinu PA10
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
 
  //Włączenie transmisji na podanych pinach
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
}


Wpisywanie i odebranie danych


Funkcja Send_Charc() pozwala na wysłanie danych z mikrokontrolera na komputer. Sprawdza ona stan bufora nadawczego. Jeśli jest on pusty następuje wysłanie danych.

Następna funkcja czyli USART_InsertToBuffer wprowadza odebrane dane do bufora.

Ostatnia z nich USART_Getc() pobiera dane przesłane z komputera.

void Send_Charc(volatile char c)
{
 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
 USART_SendData(USART1, c);
}
void USART_InsertToBuffer(uint8_t usart_num, uint8_t c)
{
 usart_num = 0;
 //Sprawdza dostepne miejsce w buforze
 if (usart_buf_num[usart_num] < 32)
 {
  if (usart_buf_in[usart_num] > (32 - 1))
  {
   usart_buf_in[usart_num] = 0;
  }
  USART_Buffer[usart_num][usart_buf_in[usart_num]] = c;
  usart_buf_in[usart_num]++;
  usart_buf_num[usart_num]++;
 }
}
uint8_t USART_Getc(USART_TypeDef* USARTx)
{
 uint8_t usart_num = 0;
 uint8_t c = 0;
 //Sprawdza czy sa dane w buforze
 if (usart_buf_num[usart_num] > 0)
 {
  if (usart_buf_out[usart_num] > (32 - 1))
  {
   usart_buf_out[usart_num] = 0;
  }
  c = USART_Buffer[usart_num][usart_buf_out[usart_num]];
  USART_Buffer[usart_num][usart_buf_out[usart_num]] = 0;
  usart_buf_out[usart_num]++;
  usart_buf_num[usart_num]--;
 }
 return c;
}


Program


Poniżej wklejam cały kod programu. Jego działanie polega na wpisaniu otrzymanych danych do bufora oraz zmiennej. Następnie następuje przesłanie ich ponownie na komputer.

#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_usart.h"
#include "misc.h"
 
//Deklaracja zmiennych
int8_t USART_Buffer[8][32];
uint16_t usart_buf_in[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint16_t usart_buf_out[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint16_t usart_buf_num[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 
void USART_Initialize(void);
void GPIO_Initialize(void);
void Send_Charc(volatile char);
void USART_InsertToBuffer(uint8_t, uint8_t);
uint8_t USART_Getc(USART_TypeDef* USARTx);
 
int main(void)
{
  uint16_t c;
  SystemInit();
  GPIO_Initialize();
  USART_Initialize();
  while (1)
  {
   c = USART_Getc(USART1);
   if(c)
   {
    Send_Charc(c);
   }
  }
}
 
void USART_Initialize(void)
{
  //konfiguracja układu USART
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStruct;
 
  NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
 
  //Włączenie zegara dla USART1
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
 
  //Ustawienie prędkości transmisji 9600bps
  USART_InitStructure.USART_BaudRate = 9600;
  //Długosc wysylanego slowa
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  //Ustawienie jednego bitu stopu
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  //Kontrola parzystości wyłączona
  USART_InitStructure.USART_Parity = USART_Parity_No;
  //Wylaczenie kontroli przepływu danych
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  //Tryb pracy linii odpowiednio odbior i nadawanie
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
  //Konfiguracja układu
  USART_Init(USART1, &USART_InitStructure);
  //Włączenie USART1
  USART_Cmd(USART1, ENABLE);
 
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //Wlaczenie przerwania dla RX
 
  //Włącznie obslugi IRQ
  NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  //Ustawienie priorytetu grupowego dla USART1 przypisane jest 0
  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
  //Podpriorytet
  NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
  NVIC_Init(&NVIC_InitStruct);
}
 
void GPIO_Initialize(void)
{
  //konfigurowanie portow GPIO
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 
  //TX dla pinu PA9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
 
  //RX dla pinu PA10
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
 
  //Włączenie transmisji na podanych pinach
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
}
void Send_Charc(volatile char c)
{
 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
 USART_SendData(USART1, c);
}
void USART_InsertToBuffer(uint8_t usart_num, uint8_t c)
{
 usart_num = 0;
 //Sprawdza dostepne miejsce w buforze
 if (usart_buf_num[usart_num] < 32)
 {
  if (usart_buf_in[usart_num] > (32 - 1))
  {
   usart_buf_in[usart_num] = 0;
  }
  USART_Buffer[usart_num][usart_buf_in[usart_num]] = c;
  usart_buf_in[usart_num]++;
  usart_buf_num[usart_num]++;
 }
}
uint8_t USART_Getc(USART_TypeDef* USARTx)
{
 uint8_t usart_num = 0;
 uint8_t c = 0;
 //Sprawdza czy sa dane w buforze
 if (usart_buf_num[usart_num] > 0)
 {
  if (usart_buf_out[usart_num] > (32 - 1))
  {
   usart_buf_out[usart_num] = 0;
  }
  c = USART_Buffer[usart_num][usart_buf_out[usart_num]];
  USART_Buffer[usart_num][usart_buf_out[usart_num]] = 0;
  usart_buf_out[usart_num]++;
  usart_buf_num[usart_num]--;
 }
 return c;
}
#ifdef USART1
void USART1_IRQHandler(void)
{
 //Sprawdzenie czy wystapilo przerwanie z powodu odebrania danych
 if (USART_GetITStatus(USART1, USART_IT_RXNE))
 {
  #ifdef USART1_USE_CUSTOM_IRQ
   //Wywołaj funckje przerwania
   USART1_ReceiveHandler(USART1->DR);
  #else
   //Podaj dane do Buffera
   USART_InsertToBuffer(0, USART1->DR);
  #endif
 }
}
#endif