Merge remote-tracking branch 'refs/remotes/origin/UART_LENNART'

This commit is contained in:
Lennart Eriksson 2016-09-30 12:21:02 +02:00
commit 3c89d4ae07
2 changed files with 612 additions and 0 deletions

View File

@ -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);
}
}
}
*/

View File

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