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; + } +}