Files

442 lines
15 KiB
C

/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file usart.c
* @brief This file provides code for the configuration
* of the USART instances.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"
/* USER CODE BEGIN 0 */
__IO circular_buffer tx_in_buffer = {0};
__IO circular_buffer tx_out_buffer = {0};
/* USER CODE END 0 */
/* USART1 init function */
void MX_USART1_UART_Init(void) {
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
LL_USART_InitTypeDef USART_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the peripherals clocks
*/
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
Error_Handler();
}
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
/**USART1 GPIO Configuration
PB6 ------> USART1_TX
PB7 ------> USART1_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USART1 DMA Init */
/* USART1_RX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_USART1_RX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);
/* USART1_TX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_2, LL_DMAMUX_REQ_USART1_TX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2,
LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MDATAALIGN_BYTE);
/* USART1 interrupt Init */
NVIC_SetPriority(USART1_IRQn, 0);
NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
USART_InitStruct.BaudRate = 115200;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART1, &USART_InitStruct);
LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_8);
LL_USART_SetRXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_8);
LL_USART_DisableFIFO(USART1);
LL_USART_ConfigAsyncMode(USART1);
/* USER CODE BEGIN WKUPType USART1 */
/* USER CODE END WKUPType USART1 */
LL_USART_Enable(USART1);
/* Polling USART1 initialisation */
while ((!(LL_USART_IsActiveFlag_TEACK(USART1))) ||
(!(LL_USART_IsActiveFlag_REACK(USART1)))) {
}
/* USER CODE BEGIN USART1_Init 2 */
usart1_hart_init();
/* USER CODE END USART1_Init 2 */
}
/* USART2 init function */
void MX_USART2_UART_Init(void) {
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
LL_USART_InitTypeDef USART_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART2 DMA Init */
/* USART2_RX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMAMUX_REQ_USART2_RX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE);
/* USART2_TX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_4, LL_DMAMUX_REQ_USART2_TX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_4,
LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MDATAALIGN_BYTE);
/* USART2 interrupt Init */
NVIC_SetPriority(USART2_IRQn, 0);
NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
USART_InitStruct.BaudRate = 115200;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART2, &USART_InitStruct);
LL_USART_ConfigAsyncMode(USART2);
/* USER CODE BEGIN WKUPType USART2 */
/* USER CODE END WKUPType USART2 */
LL_USART_Enable(USART2);
/* Polling USART2 initialisation */
while ((!(LL_USART_IsActiveFlag_TEACK(USART2))) ||
(!(LL_USART_IsActiveFlag_REACK(USART2)))) {
}
/* USER CODE BEGIN USART2_Init 2 */
usart2_hart_init();
/* USER CODE END USART2_Init 2 */
}
/* USER CODE BEGIN 1 */
/**
* @brief Initializes USART1 for bidirectional communication using DMA.
* @note This function configures USART1 to operate in both transmit (TX) and
* receive (RX) modes. It sets up the DMA channels for data transfer,
* enables idle line interrupts to detect the end of data reception,
* and configures necessary settings for efficient data transmission and
* reception.
*
* @param None
* @retval None
*/
void usart1_hart_init(void) {
// Set USART1 to operate in TX and RX mode
LL_USART_SetTransferDirection(USART1, LL_USART_DIRECTION_TX_RX);
// Enable idle line interrupt for USART1 to detect when data reception is
// complete
LL_USART_EnableIT_IDLE(USART1);
/* Configure DMA for USART1 RX */
// Set the peripheral address for DMA RX to the USART1 data register
LL_DMA_SetPeriphAddress(
DMA1, LL_DMA_CHANNEL_1,
LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE));
// Set the memory address where received data will be stored
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1,
(uint32_t)g_uart_in_rx_buf[g_uart_in_rx_index]);
// Set the amount of data to be received
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, BUFFER_SIZE);
/* Enable DMA transfer complete and transfer error interrupts for RX */
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
/* Start the DMA channel for USART1 RX and enable USART RX DMA */
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_USART_EnableDMAReq_RX(USART1);
// Clear any existing idle line flags
LL_USART_ClearFlag_IDLE(USART1);
/* Configure DMA for USART1 TX */
// Set the peripheral address for DMA TX to the USART1 data register
LL_DMA_SetPeriphAddress(
DMA1, LL_DMA_CHANNEL_2,
LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_TRANSMIT));
// Enable USART1 TX DMA request for data transmission
LL_USART_EnableDMAReq_TX(USART1);
// Enable DMA transfer complete interrupt for USART1 TX
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2);
}
/**
* @brief Initializes USART2 for bidirectional communication using DMA.
* @note This function configures USART2 to operate in both transmit (TX) and
* receive (RX) modes. It sets up the DMA channels for data transfer,
* enables idle line interrupts to signal the end of data reception,
* and configures necessary settings for effective data transmission and
* reception.
*
* @param None
* @retval None
*/
void usart2_hart_init(void) {
// Set USART2 to operate in TX and RX mode
LL_USART_SetTransferDirection(USART2, LL_USART_DIRECTION_TX_RX);
// Enable idle line interrupt for USART2 to detect the end of data reception
LL_USART_EnableIT_IDLE(USART2);
/* Configure DMA for USART2 RX */
// Set the peripheral address for DMA RX to the USART2 data register
LL_DMA_SetPeriphAddress(
DMA1, LL_DMA_CHANNEL_3,
LL_USART_DMA_GetRegAddr(USART2, LL_USART_DMA_REG_DATA_RECEIVE));
// Set the memory address for storing the received data
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_3,
(uint32_t)g_uart_out_rx_buf[g_uart_out_rx_index]);
// Set the amount of data to be received
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, BUFFER_SIZE);
/* Enable DMA transfer complete and transfer error interrupts for RX */
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_3);
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_3);
/* Start the DMA channel for USART2 RX and enable USART RX DMA */
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);
LL_USART_EnableDMAReq_RX(USART2);
// Clear any existing idle line flags
LL_USART_ClearFlag_IDLE(USART2);
/* Configure DMA for USART2 TX */
// Set the peripheral address for DMA TX to the USART2 data register
LL_DMA_SetPeriphAddress(
DMA1, LL_DMA_CHANNEL_4,
LL_USART_DMA_GetRegAddr(USART2, LL_USART_DMA_REG_DATA_TRANSMIT));
// Enable USART2 TX DMA request for data transmission
LL_USART_EnableDMAReq_TX(USART2);
// Enable DMA transfer complete interrupt for USART2 TX
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_4);
}
/**
* @brief Queue data for DMA transmission on USART1.
*
* This function always enqueues the data first.
* If the DMA is idle, it immediately starts transmission.
* If the queue is full, the packet is discarded.
*
* @param buf Pointer to the data buffer.
* @param size Number of bytes to transmit.
*/
void usart1_transmit_dma(uint8_t *buf, uint16_t size)
{
__disable_irq(); // Enter critical section
if (tx_in_buffer.packet_count < MAX_QUEUE_SIZE && size <= BUFFER_SIZE) {
// Copy data into the queue
memcpy(tx_in_buffer.send_queue[tx_in_buffer.tail].data, buf, size);
tx_in_buffer.send_queue[tx_in_buffer.tail].length = size;
// Advance tail and increment count
tx_in_buffer.tail = (tx_in_buffer.tail + 1) % MAX_QUEUE_SIZE;
tx_in_buffer.packet_count++;
// If DMA is idle, start transmission
if (g_uart_in_transmit_complete) {
uint8_t index = tx_in_buffer.head;
uint16_t len = tx_in_buffer.send_queue[index].length;
g_uart_in_transmit_complete = 0;
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)tx_in_buffer.send_queue[index].data);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, len);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
}
} else {
// Queue full, discard the packet
// Optional: increment a counter or log the event
}
__enable_irq(); // Exit critical section
}
/**
* @brief Queue data for DMA transmission on USART2.
*
* This function always enqueues the data first.
* If the DMA is idle, it immediately starts transmission.
* If the queue is full, the packet is discarded.
*
* @param buf Pointer to the data buffer.
* @param size Number of bytes to transmit.
*/
void usart2_transmit_dma(uint8_t *buf, uint16_t size)
{
__disable_irq(); // Enter critical section
if (tx_out_buffer.packet_count < MAX_QUEUE_SIZE && size <= BUFFER_SIZE) {
// Copy data into the queue
memcpy(tx_out_buffer.send_queue[tx_out_buffer.tail].data, buf, size);
tx_out_buffer.send_queue[tx_out_buffer.tail].length = size;
// Advance tail and increment count
tx_out_buffer.tail = (tx_out_buffer.tail + 1) % MAX_QUEUE_SIZE;
tx_out_buffer.packet_count++;
// If DMA is idle, start transmission
if (g_uart_out_transmit_complete) {
uint8_t index = tx_out_buffer.head;
uint16_t len = tx_out_buffer.send_queue[index].length;
g_uart_out_transmit_complete = 0;
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_4, (uint32_t)tx_out_buffer.send_queue[index].data);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_4, len);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4);
}
} else {
// Queue full, discard the packet
// Optional: increment a counter or log the event
}
__enable_irq(); // Exit critical section
}
/* USER CODE END 1 */