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.
- 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