czwartek, 12 listopada 2015

[4a] STM32 M3 - Nucleo - F103RB - USART Przerwania

Tym razem chciałbym trochę rozwinąć zagadnienia związane z wykorzystywaniem interfejsu USART, poprzez dodanie przerwań NVIC.

Wstęp


Ustawienie przerwania pozwoli mikrokontrolerowi na przerwanie wykonywanej czynności i wykonanie procedury znajdującej się w funkcji obsługi tego przerwania. Dzięki temu możliwe będzie wyświetlenie przesłanych znaków w trakcie wykonywania innych operacji w układzie.

Program


Na samym początku należy dokonać inicjalizacji układu USART, po tym następuje włączenie zgłaszania przerwania, które nastąpi po odebraniu danych przez interfejs. W tym celu stosuje się komendę USART_ITConfig(USART2, USART_IT_RXNE, ENABLE). Następnie należy skonfigurować obsługę przerwania USART2_IRQn. 

Działanie programu polega na tym, że układ będzie zgłaszał przerwanie po każdym odebraniu znaku. W funkcji obsługi przerwania znaki będą dodawane do tablicy.

#include "stm32f10x.h"
 
void GPIOInit(void);
void USARTInit(void);
void SendCharc(volatile char);
uint8_t USART_Getc(USART_TypeDef* USARTx);
 
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};
 
int main(void)
{
 uint16_t c;
 GPIOInit();
 USARTInit();
 
 while (1) {
  c = USART_Getc(USART2);
  if(c)
  {
     SendCharc(c);
  }
 }
}
 
void GPIOInit(void)
{
 GPIO_InitTypeDef GPIOInit;
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
 
 //PA2 Tx
 GPIO_StructInit(&GPIOInit);
 GPIOInit.GPIO_Pin = GPIO_Pin_2;
 GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
 GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_Init(GPIOA, &GPIOInit);
 
 //PA3 Rx
 GPIOInit.GPIO_Pin = GPIO_Pin_3;
 GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
 GPIOInit.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 GPIO_Init(GPIOA, &GPIOInit);
}
 
void USARTInit(void)
{
 USART_InitTypeDef USARTInit;
 NVIC_InitTypeDef NVIC_InitStruct;
 
 NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
 
 //Ustawienie prędkości transmisji 9600bps
 USARTInit.USART_BaudRate = 9600;
 
 //Ustawienie dlugosci slowa
 USARTInit.USART_WordLength = 8;
 
 //Ustawienie bitu stopu
 USARTInit.USART_StopBits = USART_StopBits_1;
 
 //Brak kontroli parzystosci
 USARTInit.USART_Parity = USART_Parity_No;
 
 //Kontrola przepływu danych
 USARTInit.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
 
 //Tryb pracy TX i RX
 USARTInit.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
 USART_Init(USART2, &USARTInit);
 USART_Cmd(USART2, ENABLE);
 
 USART_ITConfig(USART2, 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 SendCharc(volatile char c)
{
 while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
 USART_SendData(USART2, 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 USART2
void USART2_IRQHandler(void)
{
 //Sprawdzenie czy wystapilo przerwanie z powodu odebrania danych
 if (USART_GetITStatus(USART2, USART_IT_RXNE))
 {
  #ifdef USART2_USE_CUSTOM_IRQ
  //Wywołaj funckje przerwania
  USART2_ReceiveHandler(USART2->DR);
      #else
  //Podaj dane do Buffera
  USART_InsertToBuffer(0, USART2->DR);
      #endif
 }
}
#endif
 

Program 2


Ten program jest delikatnym rozwinięciem programu poprzedniego.  Prezentuje on jedno z podstawowego zastosowania odbieranych danych przez mikrokontroler wysyłanych z komputera.
W przypadku przesłania znaku 1 dioda zostanie zapalona, natomiast gdy zostanie wysłane 0 dioda zostanie zgaszona.  Do każdego z tych znaków zostanie wyświetlona odpowiednia wiadomość.

#include "stm32f10x.h"
 
void GPIOInit(void);
void USARTInit(void);
void SendCharc(volatile char);
void USARTSend(volatile char *c);
uint8_t USART_Getc(USART_TypeDef* USARTx);
 
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};
 
int main(void)
{
 uint16_t c;
 GPIOInit();
 USARTInit();
 
 while (1) {
  c = USART_Getc(USART2);
  if(c)
  {
      switch (c)
      {
          case '1':
           USARTSend("Odebrano 1. Dioda zapalona!\n");
           GPIO_SetBits(GPIOA, GPIO_Pin_5);
              break;
          case '0':
           USARTSend("Odebrano 0. Dioda zgaszona!\n");
           GPIO_ResetBits(GPIOA, GPIO_Pin_5);
              break;
          default:
           USARTSend("Wybierz 0 lub 1\n");
         SendCharc(c);
              break;
      }
  }
 }
}
 
void GPIOInit(void)
{
 GPIO_InitTypeDef GPIOInit;
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
 
 //PA2 Tx
 GPIO_StructInit(&GPIOInit);
 GPIOInit.GPIO_Pin = GPIO_Pin_2;
 GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
 GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_Init(GPIOA, &GPIOInit);
 
 //PA3 Rx
 GPIOInit.GPIO_Pin = GPIO_Pin_3;
 GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
 GPIOInit.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 GPIO_Init(GPIOA, &GPIOInit);
 
    //Wybranie konfigurowanego pinu
    GPIOInit.GPIO_Pin = GPIO_Pin_5;
    //Ustawienie pinu jak wyjście PushPull
    GPIOInit.GPIO_Mode = GPIO_Mode_Out_PP;
    //Inicjalizacja linii z podanymi ustawieniami
    GPIO_Init(GPIOA, &GPIOInit);
}
 
void USARTInit(void)
{
 USART_InitTypeDef USARTInit;
 NVIC_InitTypeDef NVIC_InitStruct;
 
 NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
 
 //Ustawienie prędkości transmisji 9600bps
 USARTInit.USART_BaudRate = 9600;
 
 //Ustawienie dlugosci slowa
 USARTInit.USART_WordLength = 8;
 
 //Ustawienie bitu stopu
 USARTInit.USART_StopBits = USART_StopBits_1;
 
 //Brak kontroli parzystosci
 USARTInit.USART_Parity = USART_Parity_No;
 
 //Kontrola przepływu danych
 USARTInit.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
 
 //Tryb pracy TX i RX
 USARTInit.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
 USART_Init(USART2, &USARTInit);
 USART_Cmd(USART2, ENABLE);
 
 USART_ITConfig(USART2, 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 SendCharc(volatile char c)
{
 while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
 USART_SendData(USART2, c);
}
 
void USARTSend(volatile char *c)
{
  //Pętla działa do puki będzie jakiś znak do wysłania
  while(*c)
  {
    //Sprawdza czy rejestr danych został opróżniony
    while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
    //Prześlij dane,
    USART_SendData(USART2, *c);
    *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 USART2
void USART2_IRQHandler(void)
{
 //Sprawdzenie czy wystapilo przerwanie z powodu odebrania danych
 if (USART_GetITStatus(USART2, USART_IT_RXNE))
 {
  #ifdef USART2_USE_CUSTOM_IRQ
  //Wywołaj funckje przerwania
  USART2_ReceiveHandler(USART2->DR);
      #else
  //Podaj dane do Buffera
  USART_InsertToBuffer(0, USART2->DR);
      #endif
 }
}
#endif