From d8a9adff6d3f6ccd289eb1783b6a314112322146 Mon Sep 17 00:00:00 2001 From: Lennart Eriksson Date: Fri, 30 Sep 2016 12:20:10 +0200 Subject: [PATCH] USART implementation This is an implementation of the USART but the DMA transfer is not supported, all other functionalities are working including i.e. polling, regular processor transmit and dma double buffer --- UAV-ControlSystem/inc/drivers/usart.h | 213 ++++++++++++++ UAV-ControlSystem/src/drivers/usart.c | 399 ++++++++++++++++++++++++++ 2 files changed, 612 insertions(+) create mode 100644 UAV-ControlSystem/inc/drivers/usart.h create mode 100644 UAV-ControlSystem/src/drivers/usart.c diff --git a/UAV-ControlSystem/inc/drivers/usart.h b/UAV-ControlSystem/inc/drivers/usart.h new file mode 100644 index 0000000..0448942 --- /dev/null +++ b/UAV-ControlSystem/inc/drivers/usart.h @@ -0,0 +1,213 @@ +/************************************************************************** +* NAME: usart.h +* AUTHOR: Lennart Eriksson +* PURPOSE: USART implementation for sending data +* INFORMATION: +* This file includes 2 types of USARTS, regular polling or DMA based +* the polling version of the USART uses the processor in order to get +* messages while the DMA has Direct Memory Access and does not need the +* processor to receive the messages and can copy the entire message once. +* The DMA is implemented using a double buffer with fixed sizes of the +* buffers in order to work with fixed data sizes of the messages. The up +* side of this is that the system can read a complete message and not +* where the DMA is not writing on the same place. Though this means that +* the message sizes needs to be know on before hand +* +* GLOBAL VARIABLES: +* Variable Type Description +* -------- ---- ----------- +**************************************************************************/ + +#ifndef DRIVERS_USART_H_ +#define DRIVERS_USART_H_ + +#include "stm32f4xx.h" + +// Enumeration for USART stop bits +typedef enum stop_bits +{ + STOP_BITS_1 = 0, + STOP_BITS_0_5, + STOP_BITS_2, + STOP_BITS_1_5 +} stop_bits; + +// Enuymeration for USART parity +typedef enum partiy +{ + PARITY_NONE = 0x0, + PARITY_EVEN = 0x2, + PARITY_ODD = 0x3 +} parity; + +// Struct to be used for regular USART with polling +typedef struct usart_profile +{ + USART_TypeDef* usart_instance; // The USART used to send or receive data +} usart_profile; + +// struct to be used for dma receiving of USART messages +typedef struct usart_dma_profile +{ + usart_profile usart_pro; // The USART profile to be used + DMA_Stream_TypeDef* dma_usart_rx_instance; // The DMA profile corresponding to the rx buffer + DMA_Stream_TypeDef* dma_usart_tx_instance; // The DMA profile corresponding to the tx buffer + uint8_t* dma_rx_buffer1; // The first rx buffer used in double buffering + uint8_t* dma_rx_buffer2; // The second rx buffer used in double buffering + uint8_t* dma_tx_buffer; // The tx buffer used for sending messages +} usart_dma_profile; + + + +/*********************************************************************** +* BRIEF: Initialize the USART with DMA reception of messages +* INFORMATION: +* Initialize the specified USART and enable the DMA for it so that the +* messages can be received without utilizing any processor load. This +* function returns false if any error occurred during the initialization +* and true of everything went well +***********************************************************************/ +bool usart_init_dma(USART_TypeDef* usart_inst, + usart_dma_profile* profile_out, + uint32_t baud_rate, + stop_bits stopbits, + parity parity_mode, + uint32_t dma_rx_buffersize, + uint32_t dma_tx_buffersize); + + +/*********************************************************************** +* BRIEF: Initialize a regular USART that can be used for polling +* INFORMATION: +* Initialize the specified USART in order to get polling and regular +* transmit of messages to work. If the initialization fails this function +* returns false and otherwise it returns true +***********************************************************************/ +bool usart_init(USART_TypeDef* usart_inst, + usart_profile* profile_out, + uint32_t baud_rate, + stop_bits stopbits, + parity parity_mode); + +/*********************************************************************** +* BRIEF: Send message over USART +* INFORMATION: +* Try to send a message over USART, if the time exceeds the specified +* timeout the transmit will be stopped. This function returns the number +* of bytes that was successfully sent down to the USART bus even if +* the timeout was reached before it was done. +***********************************************************************/ +uint32_t usart_transmit(usart_profile *profile, + uint8_t* buffer, + uint32_t size, + uint32_t timeout_us); + +/*********************************************************************** +* BRIEF: return if the USART has any data to be received in the buffer +* INFORMATION: +***********************************************************************/ +bool usart_poll_data_ready(usart_profile* profile); + +/*********************************************************************** +* BRIEF: Poll messages from the USART +* INFORMATION: +* Try to get a message from the USART, if the time spent receiving +* exceeds the specified timeout the function will return the buffer +* that has been received to that point. The function returns the number +* of bytes received even if the timeout was exceeded. +***********************************************************************/ +uint32_t usart_poll(usart_profile *profile, + uint8_t* buffer, + uint32_t size, + uint32_t timeout_us); + +/*********************************************************************** +* BRIEF: Get the DMA buffer that was most recently completed +* INFORMATION: +* This function will return the buffer that the DMA most recently +* completed so that the DMA can continue writing to the second buffer +* without interfering with the rest of the system +***********************************************************************/ +uint8_t* usart_get_dma_buffer(usart_dma_profile *profile); + +/*********************************************************************** +* BRIEF: NOT IMPLEMENTED YET +* INFORMATION: +* Use the usart_transmit function instead with the +* usart_dma_profile.usart_pro as input argument instead +***********************************************************************/ +void usart_transmit_dma(usart_dma_profile *profile, + uint8_t* buffer, + uint32_t size); + +#endif /* DRIVERS_USART_H_ */ + +// -------------------- EXAMPLE OF USART POLL ------------------------- +/* +int main() +{ + //Initialize the system + init_system(); + + //Set up a usart profile + usart_profile usart_p; + + //Initiate the profile for specified USART + usart_init(USART1, &usart_p, 115200, STOP_BITS_1, PARITY_NONE); + + //The data buffer used + uint8_t data = 0x00; + + while(1) + { + //poll data from the USART + uint32_t num_received = usart_poll(&usart_p, &data, 1, 1000); + //if data was received send the data back over the USART + if(num_received > 0x00) + { + usart_transmit(&usart_p, &data, 1, 1000); + } + } +} +*/ + +// -------------------- EXAMPLE OF USART DMA ------------------------- +/* +#define USART_DMA_SIZE 4 + +int main(void) +{ + //Init the system + init_system(); + + //Set up a usart DMA profile + usart_dma_profile dma_p; + + //Initiate USART DMA profile + usart_init_dma(USART1, &dma_p, 115200, STOP_BITS_1, PARITY_NONE, USART_DMA_SIZE, 0); + + //Temporary array to compare against + uint8_t tmp[USART_DMA_SIZE] = { 0x00 }; + + while(1) + { + uint8_t* buf; + // Get the dma finished buffer + buf = usart_get_dma_buffer(&dma_p); + bool change = false; + //compare against the previous buffer and copy the elements + for(int i = 0; i < USART_DMA_SIZE; i++) + { + if(tmp[i] != (tmp[i] = buf[i])) + change |= true; + } + + //if the buffer has changed print the buffer back over USART + if(change) + { + usart_transmit(&dma_p, tmp, USART_DMA_SIZE, 1000); + } + } +} +*/ + diff --git a/UAV-ControlSystem/src/drivers/usart.c b/UAV-ControlSystem/src/drivers/usart.c new file mode 100644 index 0000000..37e5eeb --- /dev/null +++ b/UAV-ControlSystem/src/drivers/usart.c @@ -0,0 +1,399 @@ +/************************************************************************** +* NAME: usart.c +* AUTHOR: Lennart Eriksson +* PURPOSE: USART implementation for sending data +* INFORMATION: +* This file includes 2 types of USARTS, regular polling or DMA based +* the polling version of the USART uses the processor in order to get +* messages while the DMA has Direct Memory Access and does not need the +* processor to receive the messages and can copy the entire message once. +* The DMA is implemented using a double buffer with fixed sizes of the +* buffers in order to work with fixed data sizes of the messages. The up +* side of this is that the system can read a complete message and not +* where the DMA is not writing on the same place. Though this means that +* the message sizes needs to be know on before hand +* +* GLOBAL VARIABLES: +* Variable Type Description +* -------- ---- ----------- +**************************************************************************/ + +#include "drivers/usart.h" +#include "stm32f4xx_revo.h" +#include "drivers/system_clock.h" + + +//Define registers for the USART since the HAL library does not work with sending +//data at the moment + +// CR1 +#define UE 0x2000 // Usart Enabled +#define M 0x0000 // word length 8 +#define RE 0x0004 // Receive enabled +#define TE 0x0008 // Transmit enabled +#define PARITY_OFFSET 9 //Offset to parity bits + +//CR2 +#define STOP_OFFSET 12 // offset to stop bits in CR2 + +//CR3 +#define DMAR 0x0040 // Enable DMA rx +#define DMAT 0x0080 // Enable DMA tx + +//BRR +#define USART_BRR(_PCLK_, _BAUD_) ((_PCLK_ /(_BAUD_ * 16)) * 16) // Calculate BRR from the desired baud rate + + +/*********************************************************************** +* BRIEF: Initialize the USART with DMA reception of messages +* INFORMATION: +* Initialize the specified USART and enable the DMA for it so that the +* messages can be received without utilizing any processor load. This +* function returns false if any error occurred during the initialization +* and true of everything went well +***********************************************************************/ +bool usart_init_dma(USART_TypeDef* usart_inst, // The USART instance to be used, i.e. USART1, USART3 or USART6 for the REVO card + usart_dma_profile* profile_out, // The USART profile that will be used when sending or receiving data + uint32_t baud_rate, // The baud rate that the USART will communicate with + stop_bits stopbits, // The number of stop bits that the USART should have + parity parity_mode, // The parity that the USART should have + uint32_t dma_rx_buffersize, // The buffer size for the DMA rx buffers + uint32_t dma_tx_buffersize) // The buffer size for the DMA tx buffers +{ + // Local variables used when extracting the different USARTs + DMA_Stream_TypeDef *dma_rx_instance, *dma_tx_instance; + uint32_t channel; + + // Check if the instance is either USART1, USART3 of USART6 since + // those are the only ones available on the REVO card + if(usart_inst == USART1) + { + dma_rx_instance = DMA2_Stream2; + dma_tx_instance = DMA2_Stream5; + channel = DMA_CHANNEL_4; + } + else if(usart_inst == USART3) + { + dma_rx_instance = DMA1_Stream1; + dma_tx_instance = DMA1_Stream3; + channel = DMA_CHANNEL_4; + } + else if(usart_inst == USART6) + { + dma_rx_instance = DMA2_Stream2; + dma_tx_instance = DMA2_Stream6; + channel = DMA_CHANNEL_5; + } + else + return false; // If any other USART is sent in return false + + // Enable the correct clock if it has not been enabled already + if(__HAL_RCC_DMA2_IS_CLK_DISABLED() && (usart_inst == USART6 || usart_inst == USART1)) + __HAL_RCC_DMA2_CLK_ENABLE(); + if(__HAL_RCC_DMA1_IS_CLK_DISABLED()&& usart_inst == USART3) + __HAL_RCC_DMA1_CLK_ENABLE(); + + // Initialize the regular usart before adding on the DMA + if(!usart_init(usart_inst, &profile_out->usart_pro, baud_rate, stopbits, parity_mode)) + return false; // If the initialization did not complete return false + + // set the USART profile buffers and initialize them to 0x00 for every element + profile_out->dma_rx_buffer1 = malloc(sizeof(uint8_t) * dma_rx_buffersize); + profile_out->dma_rx_buffer2 = malloc(sizeof(uint8_t) * dma_rx_buffersize); + profile_out->dma_tx_buffer = malloc(sizeof(uint8_t) * dma_tx_buffersize); + memset(profile_out->dma_rx_buffer1, 0x00, dma_rx_buffersize); + memset(profile_out->dma_rx_buffer2, 0x00, dma_rx_buffersize); + memset(profile_out->dma_tx_buffer, 0x00, dma_tx_buffersize); + + // Set the DMA instances in the USART profile + profile_out->dma_usart_rx_instance = dma_rx_instance; + profile_out->dma_usart_tx_instance = dma_tx_instance; + + // Enable the DMA on the USARTon register level + profile_out->usart_pro.usart_instance->CR3 |= DMAR | DMAT; + + // This is only a dummy that is used by the DMA linking later on + USART_HandleTypeDef usart; + usart.Instance = usart_inst; + + // Set up the DMA handle for the USART rx + DMA_HandleTypeDef g_DmaHandle_rx; + g_DmaHandle_rx.Instance = dma_rx_instance; // the rx instance + g_DmaHandle_rx.Init.Channel = channel; // the rx channel + g_DmaHandle_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; // set data direction to peripheral to memory + g_DmaHandle_rx.Init.PeriphInc = DMA_PINC_DISABLE; // peripheral increment data pointer disabled + g_DmaHandle_rx.Init.MemInc = DMA_MINC_ENABLE; // Memory increment data pointer enabled + g_DmaHandle_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; // Align data words on the peripheral + g_DmaHandle_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; // Align data words on the memory + g_DmaHandle_rx.Init.Mode = DMA_SxCR_DBM; // Enable Double buffer mode + g_DmaHandle_rx.Init.Priority = DMA_PRIORITY_HIGH; // Set the receive priority to high + g_DmaHandle_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; // Disable fifo mode + g_DmaHandle_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL; // Set fifo threshold to half full although this is probably not used + g_DmaHandle_rx.Init.MemBurst = DMA_MBURST_SINGLE; // In double buffer mode the burst must always be single + g_DmaHandle_rx.Init.PeriphBurst = DMA_PBURST_SINGLE; // In double buffer mode the burst must always be single + + // Initialize the DMA handle + HAL_DMA_Init(&g_DmaHandle_rx); + + // Link the DMA to the USART instance + __HAL_LINKDMA(&usart, hdmarx ,g_DmaHandle_rx); + + + //Set up the DMA handle for the USART tx + DMA_HandleTypeDef g_DmaHandle_tx; + g_DmaHandle_tx.Instance = dma_tx_instance; + g_DmaHandle_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + g_DmaHandle_tx.Init.Channel = channel; + HAL_DMA_Init(&g_DmaHandle_tx); + __HAL_LINKDMA(&usart, hdmatx ,g_DmaHandle_tx); + + +// g_DmaHandle.Instance = dma_tx_instance; +// g_DmaHandle.Init.Direction = DMA_MEMORY_TO_PERIPH; +// g_DmaHandle.Init.Mode = 0x00; +// g_DmaHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE; +// g_DmaHandle.Init.Priority = DMA_PRIORITY_MEDIUM; +// +// HAL_DMA_Init(&g_DmaHandle); +// +// __HAL_LINKDMA(&usart, hdmatx ,g_DmaHandle); + + + + //Setup DMA buffers + + // Disable the DMA, must be done before writing to the addresses below + dma_rx_instance->CR &= ~(DMA_SxCR_EN); + + dma_rx_instance->NDTR = dma_rx_buffersize; // Set the buffer size + dma_rx_instance->PAR = (uint32_t)&profile_out->usart_pro.usart_instance->DR; // Set the address to the USART data register + dma_rx_instance->M0AR = (uint32_t)profile_out->dma_rx_buffer1; // Set the address to the first DMA buffer + dma_rx_instance->M1AR = (uint32_t)profile_out->dma_rx_buffer2; // Set the address to the second DMA buffer + dma_rx_instance->CR &= ~(0xF << 11); // Set the data size to 8 bit values + + //Enable the DMA again to start receiving data from the USART + dma_rx_instance->CR |= DMA_SxCR_EN; + + + dma_tx_instance->CR &= ~(DMA_SxCR_EN); + + dma_tx_instance->NDTR = dma_tx_buffersize; + dma_tx_instance->PAR = (uint32_t)&profile_out->usart_pro.usart_instance->DR; + dma_tx_instance->M0AR = (uint32_t)profile_out->dma_tx_buffer; + dma_tx_instance->CR &= ~(0xF << 11); + + + dma_tx_instance->CR |= DMA_SxCR_EN; + + return true; // everything went as planned and the USART should be ready to use +} + +/*********************************************************************** +* BRIEF: Initialize a regular USART that can be used for polling +* INFORMATION: +* Initialize the specified USART in order to get polling and regular +* transmit of messages to work. If the initialization fails this function +* returns false and otherwise it returns true +***********************************************************************/ +bool usart_init(USART_TypeDef* usart_inst, // The USART instance to be used, i.e. USART1, USART3 or USART6 for the REVO board + usart_profile* profile_out, // The USART profile that will be used when sending or receiving data + uint32_t baud_rate, // The baud rate that the USART will communicate with + stop_bits stopbits, // The number of stop bits that the USART should have + parity parity_mode) // The parity that the USART should have +{ + // set the USART profiles USART instance + profile_out->usart_instance = usart_inst; + + // Local variables used when extracting the different USARTs + uint32_t rx_pin, tx_pin, af_func; + GPIO_TypeDef* gpioport; + + // Check if the instance is either USART1, USART3 of USART6 since + // those are the only ones available on the REVO card + if(usart_inst == USART1) + { + rx_pin = USART1_RX_PIN; + tx_pin = USART1_TX_PIN; + af_func = GPIO_AF7_USART1; + gpioport = USART1_RX_PORT; + + //Enable clock if not already enabled + if(__HAL_RCC_USART1_IS_CLK_DISABLED()) + __HAL_RCC_USART1_CLK_ENABLE(); + } + else if(usart_inst == USART3) + { + rx_pin = USART3_RX_PIN; + tx_pin = USART3_TX_PIN; + af_func = GPIO_AF7_USART3; + gpioport = USART3_RX_PORT; + + //Enable clock if not already enabled + if(__HAL_RCC_USART3_IS_CLK_DISABLED()) + __HAL_RCC_USART3_CLK_ENABLE(); + } + else if(usart_inst == USART6) + { + rx_pin = USART6_RX_PIN; + tx_pin = USART6_TX_PIN; + af_func = GPIO_AF8_USART6; + gpioport = USART6_RX_PORT; + + //Enable clock if not already enabled + if(__HAL_RCC_USART6_IS_CLK_DISABLED()) + __HAL_RCC_USART6_CLK_ENABLE(); + } + else + return false;// If any other USART is sent in return false + + + // PIN Initialization for the USART + GPIO_InitTypeDef gpio; + gpio.Pin = rx_pin | tx_pin; + gpio.Pull = GPIO_NOPULL; + gpio.Mode = GPIO_MODE_AF_PP; + gpio.Speed = GPIO_SPEED_HIGH; + gpio.Alternate = af_func; + HAL_GPIO_Init(gpioport, &gpio); + + // USART initialization + // This is on register level since the HAL library did not work as expected + usart_inst->CR1 = UE | M | RE | TE | (parity_mode << PARITY_OFFSET); + usart_inst->CR2 = stopbits << STOP_OFFSET; + usart_inst->BRR = USART_BRR(HAL_RCC_GetPCLK2Freq(), baud_rate); + + return true; // Everything went as planned and the USART is enabled +} + +/*********************************************************************** +* BRIEF: Send message over USART +* INFORMATION: +* Try to send a message over USART, if the time exceeds the specified +* timeout the transmit will be stopped. This function returns the number +* of bytes that was successfully sent down to the USART bus even if +* the timeout was reached before it was done. +***********************************************************************/ +uint32_t usart_transmit(usart_profile *profile, // The USART profile to send data from + uint8_t* buffer, // The buffer to send + uint32_t size, // The size of the buffer to send + uint32_t timeout_us) // If time exceeds this microsecond value the function will return +{ + // Calculate at what time the function should stop sending and return if not done + uint32_t time_to_leave = clock_get_us() + timeout_us; + int i; + // Send all messages in the buffer + for(i = 0; i < size; i++) + { + profile->usart_instance->DR = buffer[i]; + // Wait for the buffer to be emptied and sent over the usart, if the timeout value is reached leave this function + while (!(profile->usart_instance->SR & 0x40) && time_to_leave > clock_get_us()); + + // If the overrun error is active clear it before continue + if(profile->usart_instance->SR & 0x08) + { + profile->usart_instance->SR; + profile->usart_instance->DR; + } + + // if the timeout is reached return the number of bytes that was successfully sent over USART + if(time_to_leave <= clock_get_us()) + return i; + } + + //return the number of Bytes sent on the USART, this should be the same size as the provided buffer size + return i; +} + +/*********************************************************************** +* BRIEF: NOT IMPLEMENTED YET +* INFORMATION: +* Use the usart_transmit function instead with the +* usart_dma_profile.usart_pro as input argument instead +***********************************************************************/ +void usart_transmit_dma(usart_dma_profile *profile, uint8_t* buffer, uint32_t size) +{ + // this is only a try to get the system working so there is no definite proof that this + // is the correct way. This is only kept if it were to be implemented to see what have been + // done. + + /* + for(int i = 0; i < size; i++) + { + profile->dma_tx_buffer[i] = buffer[i]; + } + + profile->dma_usart_tx_instance->CR &= ~(DMA_SxCR_EN); + + profile->dma_usart_tx_instance->NDTR = size; + + profile->dma_usart_tx_instance->CR |= DMA_SxCR_EN; + */ +} + +/*********************************************************************** +* BRIEF: return if the USART has any data to be received in the buffer +* INFORMATION: +***********************************************************************/ +bool usart_poll_data_ready(usart_profile* profile) +{ + // check if the Read Data Register Not Empty (RXNE) is set + if(profile->usart_instance->SR & 0x20) + return true; + + return false; +} + +/*********************************************************************** +* BRIEF: Poll messages from the USART +* INFORMATION: +* Try to get a message from the USART, if the time spent receiving +* exceeds the specified timeout the function will return the buffer +* that has been received to that point. The function returns the number +* of bytes received even if the timeout was exceeded. +***********************************************************************/ +uint32_t usart_poll(usart_profile *profile, // The USART profile to receive data from + uint8_t* buffer, // The buffer to put the data in + uint32_t size, // The expected size of the data + uint32_t timeout_us) // If time exceeds this microsecond value the function will return +{ + // Calculate at what time the function should stop sending and return if not done + uint32_t time_to_leave = clock_get_us() + timeout_us; + + int i = 0; + // While the timeout is not exceeded and we have data to read run this loop + while(time_to_leave > clock_get_us() && i < size) + { + // Check if data is ready to be received + if(usart_poll_data_ready(profile)) + { + // Copy the data from the data register to the buffer + buffer[i++] = profile->usart_instance->DR & 0xFF; + + // Wait until the status register gets ready again + while (profile->usart_instance->SR & 0x20); + } + } + + //return the number of bytes received + return i; +} + +/*********************************************************************** +* BRIEF: Get the DMA buffer that was most recently completed +* INFORMATION: +* This function will return the buffer that the DMA most recently +* completed so that the DMA can continue writing to the second buffer +* without interfering with the rest of the system +***********************************************************************/ +uint8_t* usart_get_dma_buffer(usart_dma_profile *profile) +{ + // Check which buffer the DMA is writing to at the moment and return the other buffer + if(profile->dma_usart_rx_instance->CR & DMA_SxCR_CT) + { + return profile->dma_rx_buffer1; + } + else + { + return profile->dma_rx_buffer2; + } +}