Files
CHJ/user/MCU/lhl_uart.c
2026-03-20 21:19:53 +08:00

324 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "../main/SystemInclude.h"
DMA_HandleTypeDef DMA_Handle_UartRx , DMA_Handle_UartTx ;
/*********************************************************************************************************************************************/
/*初始化*/
/*********************************************************************************************************************************************/
/**------------------------------------------------------------------------
* @brief 初始化 UART0配置引脚和通信参数
* @param baudrate_bps: 波特率,单位 bps如 115200
* @note 使用 P0.1 作为 RX复用 SIN0P0.2 作为 TX复用 SOUT0
* 数据格式8位数据、1位停止位、无校验。
* @example UART0_Init(115200);
**/
void UART0_Init(uint32_t baudrate_bps)
{
GPIO_InitTypeDef GPIO_InitStructure;
UART_InitTypeDef UART_InitStructure;
/* 1. 设置 UART GPIO复用引脚 */
GPIO_InitStructure.Pin = GPIO_PIN_1;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Current = GPIO_CURRENT_4mA;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.SchmittTrigger = ENABLE;
GPIO_InitStructure.Alternate = GPIO0_1_AF_SIN0;
LHL_GPIO_Init(pGPIO0, &GPIO_InitStructure); // P0.1 -> SIN0(UART_RX)
GPIO_InitStructure.Pin = GPIO_PIN_2;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Current = GPIO_CURRENT_4mA;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.SchmittTrigger = DISABLE;
GPIO_InitStructure.Alternate = GPIO0_2_AF_SOUT0;
LHL_GPIO_Init(pGPIO0, &GPIO_InitStructure); // P0.2 -> SOUT0(UART_TX)
/* 2. 设置UART参数 */
UART_InitStructure.UART_BaudRate = baudrate_bps; // 硬件最高波特率需小于时钟频率/16
UART_InitStructure.UART_WordLength = UART_WordLength_8b; // 数字位数8
UART_InitStructure.UART_StopBits = UART_StopBits_1; // 停止位数1
UART_InitStructure.UART_Parity = UART_Parity_None; // 校验位:无
LHL_UART_Init(pUART0, &UART_InitStructure);
//pUART0->IER_DLH = 0x1;
/*
UART_IT_EDSSI 调制解调器状态中断
UART_IT_ELSI 接收线路状态中断
UART_IT_ETBEI 发送中断
UART_IT_ERBFI 接收中断
*/
// LHL_UART_ITConfig( pUART0 ,UART_IT_EDSSI|UART_IT_ELSI|UART_IT_ETBEI|UART_IT_ERBFI , ENABLE);
// NVIC_SetPriority(UART0_IRQn,1);
// NVIC_EnableIRQ(UART0_IRQn);
}
/**------------------------------------------------------------------------
* @brief 初始化 UART1配置引脚和通信参数
* @param baudrate_bps: 波特率,单位 bps如 115200
* @note 使用 P1.0 作为 RX复用 SIN1P1.1 作为 TX复用 SOUT1
* 数据格式8位数据、1位停止位、无校验。
* @example UART1_Init(115200);
**/
void UART1_Init(uint32_t baudrate_bps)
{
GPIO_InitTypeDef GPIO_InitStructure;
UART_InitTypeDef UART_InitStructure;
/* 1. 设置 UART GPIO复用引脚 */
GPIO_InitStructure.Pin = GPIO_PIN_0;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Current = GPIO_CURRENT_4mA;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.SchmittTrigger = ENABLE;
GPIO_InitStructure.Alternate = GPIO1_0_AF_SIN1;
LHL_GPIO_Init(pGPIO1, &GPIO_InitStructure); // P0.1 -> SIN0(UART_RX)
GPIO_InitStructure.Pin = GPIO_PIN_1;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Current = GPIO_CURRENT_4mA;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.SchmittTrigger = DISABLE;
GPIO_InitStructure.Alternate = GPIO1_1_AF_SOUT1;
LHL_GPIO_Init(pGPIO1, &GPIO_InitStructure); // P0.2 -> SOUT0(UART_TX)
/* 2. 设置UART参数 */
UART_InitStructure.UART_BaudRate = baudrate_bps; // 硬件最高波特率需小于时钟频率/16
UART_InitStructure.UART_WordLength = UART_WordLength_8b; // 数字位数8
UART_InitStructure.UART_StopBits = UART_StopBits_1; // 停止位数1
UART_InitStructure.UART_Parity = UART_Parity_None; // 校验位:无
LHL_UART_Init(pUART1, &UART_InitStructure);
/*
UART_IT_EDSSI 调制解调器状态中断
UART_IT_ELSI 接收线路状态中断
UART_IT_ETBEI 发送中断
UART_IT_ERBFI 接收中断
*/
// LHL_UART_ITConfig( pUART1 ,UART_IT_EDSSI|UART_IT_ELSI|UART_IT_ETBEI|UART_IT_ERBFI , ENABLE);
// NVIC_SetPriority(UART1_IRQn,1);
// NVIC_EnableIRQ(UART1_IRQn);
}
/*********************************************************************************************************************************************/
/*中断方式*/
/*********************************************************************************************************************************************/
/**------------------------------------------------------------------------
* @brief UART0 中断服务函数
* @note 当前仅预留框架,未实现具体处理逻辑。
* 可根据需要在此处添加接收/发送中断处理代码。
**/
void UART0_IRQHandler(void)
{
/* 收发数据读写RBR_THR_DLL寄存器
* - 读取时:作为接收缓冲寄存器(RBR)存储UART接收到的数据
* - 写入时:作为发送保持寄存器(THR),用于发送数据 */
// if (LHL_UART_GetFlag(pUART0, UART_FLAG_DR) == SET) // 判断接收中断
// if ((LHL_UART_GetFlag(pUART0, UART_FLAG_THRE) == SET) && \
// (LHL_UART_GetPending(pUART0, UART_IT_ETBEI) == SET)) // 判断发送中断
if (LHL_UART_GetPending(pUART0, UART_IT_RXNE) == SET) // 判断接收中断
{
// uart_buffer[index++] = LHL_UART_ReceiveData(pUART0); // 放入缓存
}
if (LHL_UART_GetPending(pUART0, UART_IT_TXE) == SET)
{
// if(index) // 如果缓存还有数据
// {
// LHL_UART_SendData(pUART0, uart_buffer[--index]);
// }
// else
// {
// LHL_UART_SendData(pUART0, 0x00); // Send NULL
// LHL_UART_ITConfig(pUART0, UART_IT_ETBEI, DISABLE); // 关闭发送中断
// }
}
}
/**------------------------------------------------------------------------
* @brief UART1 中断服务函数
* @note 当前仅预留框架,未实现具体处理逻辑。
* 可根据需要在此处添加接收/发送中断处理代码。
**/
void UART1_IRQHandler(void)
{
if (LHL_UART_GetPending(pUART1, UART_IT_RXNE) == SET) // 判断接收中断
{
// uart_buffer[index++] = LHL_UART_ReceiveData(pUART0); // 放入缓存
}
if (LHL_UART_GetPending(pUART1, UART_IT_TXE) == SET)
{
}
}
/*********************************************************************************************************************************************/
/*DMA 方式*/
/*********************************************************************************************************************************************/
/**------------------------------------------------------------------------
* @brief 基础定时器中断回调函数(与 DMA_UART_AnyLength_Rx_Init 配合使用)
* @note 当 DMA 接收超时(空闲帧)时调用,设置接收完成标志,
* 停止基础定时器和 DMA 通道,并清除相关标志。
**/
void Btimer_irq_callback(void)
{
comState.state.ReceivedData = 1 ; //接收完成标志位
StopBtimer0(); //FY
LHL_DMA_Stop(&DMA_Handle_UartRx); //FY 关闭DMA接收数据
LHL_DMA_Stop(&DMA_Handle_UartTx); //FY 关闭DMA接收数据
//LHL_DMA_ClearPending(&DMA_Handle_UartRx);
//LHL_DMA_ClearPending(&DMA_Handle_UartTx);
}
//接收--------------------------------------------------------------------
/**------------------------------------------------------------------------
* @brief UART DMA 不定长接收初始化
* @param baudrate_bps: 波特率,用于计算空闲帧超时时间
* @note 使用 DMA 通道 UART_Rx 搬运数据,通过基础定时器监控空闲帧间隔。
* 超时时间由宏 IDLE_FRAME_INTERVAL 和系统时钟计算。
* 配置了两个 TCD 描述符用于控制基础定时器的启动/停止。
* 初始化后自动启动 DMA 接收。
* @example DMA_UART_AnyLength_Rx_Init(115200);
**/
void DMA_UART_AnyLength_Rx_Init( uint32_t baudrate_bps)
{
DMA_HandleTypeDef DMA_Handle_BTime;
DMA_InitTypeDef DMA_InitStructure[TCD_COUNT]; /* 分散聚合模式的多个初始化结构体参数 */
static DMA_DES_N_TypeDef TCD_Quene[TCD_COUNT] __ALIGN(32);/* DMA TCD描述符必须32位对齐*/
static uint8_t BTimerCTRLValue[2]; /* 定时器配置参数 */
uint16_t IdleFrameValue;
/* 1 配置DMAUX 请求源UART0 RX触发MA0 */
DMA_DMAMUX_CFG(DMA_CHANNEL_UART_Rx,REQUEST_SOURCE_UART_RX);//将DMAMUX链接到串口接收通道(DMA通道0)
LHL_DMA_Stop(&DMA_Handle_UartRx);
DMA_Handle_UartRx.Channel = DMA_CHANNEL_UART_Rx; //配置DMA0请求源来自DMAMUX UART_RX用于搬运UART RX数据
DMA_Handle_UartRx.Request = DMA_HARDWARE_REQUEST;
DMA_Handle_UartRx.Mode = DMA_CHAINING_MODE;
DMA_Handle_UartRx.Init.Direction = DMA_PERIPH_TO_MEMORY; // 外设->内存
DMA_Handle_UartRx.Init.Src_Address = (uint32_t)(&DMA_UART->RBR_THR_DLL);
DMA_Handle_UartRx.Init.Dest_Address = (uint32_t)comState.RxdData; // 串口数据缓存区
DMA_Handle_UartRx.Init.Data_Width = DMA_DATA_WIDTH_1B;
DMA_Handle_UartRx.Init.Data_Size = 1;
DMA_Handle_UartRx.Init.Repetition = RXD_MAX_DATA;
DMA_Handle_UartRx.Init.Trans_Mode = DMA_SINGLE_TRANSMISSION;
DMA_Handle_UartRx.Init.Chaining = (DMA_CHAINING_t)DMA_CHANNEL_BtimCfg ;//理论上应该写TO_DMA_CHANNEL_x (x=01/2/3/NONE) 不过这样写简便以适应DMA_CHANNEL_BtimCfg 改变的情况
if (LHL_DMA_Init(&DMA_Handle_UartRx) != LHL_OK)
{
while(1);
}
/*1 初始化BTIM TImer Base用于串口空闲帧超时监控 新增ADC使用DMA序列采样流程 */
/*2 计算空闲间隔超时时间为IDEL_FRAME_INTERVAL个数据位 */
/*3 配置BTIMER CTRL寄存器参数DMA1负责写入 */
/*4 开启BTIM1中断产生中断表示串口接收到一帧数据且空闲时间超过空闲间隔设定 */
BTimerCTRLValue[0] = 0;
BTimerCTRLValue[1] = BTIM_TCTRL_TEN_Msk | BTIM_TCTRL_TIE_Msk;
IdleFrameValue = (uint16_t)(SystemCoreClock * IDLE_FRAME_INTERVAL / baudrate_bps) - 1;
BTIM0_Init(IdleFrameValue);
Btimer_register_irq_callback(BTIMER_0,Btimer_irq_callback); //注册中断函数,用于进入空闲帧判断
DMA_Handle_BTime.Channel = DMA_CHANNEL_BtimCfg; //配置DMA通道1由DMA0触发用于搬运BTIM设置参数
DMA_Handle_BTime.Request = DMA_HARDWARE_REQUEST;
DMA_Handle_BTime.Mode = DMA_SCATTER_GATHER_MODE; // 分散-聚合模式
DMA_Handle_BTime.TCD_Count = TCD_COUNT; // TCD描述符数量
DMA_Handle_BTime.TCD_List = TCD_Quene; // TCD描述符链表
DMA_Handle_BTime.TCD_Init = DMA_InitStructure; // TCD初始化队列
DMA_InitStructure[0].Direction = DMA_MEMORY_TO_PERIPH; // TCD0用于复位BTIM寄存器
DMA_InitStructure[0].Src_Address = (uint32_t)&BTimerCTRLValue[0];
DMA_InitStructure[0].Dest_Address = (uint32_t)(&BTIM->TCTRL_0);
DMA_InitStructure[0].Data_Width = DMA_DATA_WIDTH_1B;
DMA_InitStructure[0].Data_Size = 1;
DMA_InitStructure[0].Repetition = 1;
DMA_InitStructure[0].Trans_Mode = DMA_CYCLIC_TRANSMISSION;
DMA_InitStructure[0].TCD_Address = (int32_t)&TCD_Quene[1];
DMA_InitStructure[0].Auto_Start = DISABLE;
DMA_InitStructure[0].INT_Major = DISABLE;
DMA_InitStructure[0].INT_Half = DISABLE;
DMA_InitStructure[0].Chaining = TO_DMA_CHANNEL_NONE;
DMA_InitStructure[1].Direction = DMA_MEMORY_TO_PERIPH; // TCD1用于重启BTIM寄存器
DMA_InitStructure[1].Src_Address = (uint32_t)&BTimerCTRLValue[1];
DMA_InitStructure[1].Dest_Address = (uint32_t)(&BTIM->TCTRL_0);
DMA_InitStructure[1].Data_Width = DMA_DATA_WIDTH_1B;
DMA_InitStructure[1].Data_Size = 1;
DMA_InitStructure[1].Repetition = 1;
DMA_InitStructure[1].Trans_Mode = DMA_CYCLIC_TRANSMISSION;
DMA_InitStructure[1].TCD_Address = (int32_t)&TCD_Quene[0];
DMA_InitStructure[1].Auto_Start = ENABLE;
DMA_InitStructure[1].INT_Major = DISABLE;
DMA_InitStructure[1].INT_Half = DISABLE;
DMA_InitStructure[1].Chaining = TO_DMA_CHANNEL_NONE;
if (LHL_DMA_Init(&DMA_Handle_BTime) != LHL_OK)
{
while(1);
}
/*DMA 配置完成开始接收*/
LHL_DMA_Start(&DMA_Handle_UartRx); /* 显式启动DMA0 ,开始接收*/
}
/**------------------------------------------------------------------------
* @brief 获取 DMA 接收的实际数据长度
* @note 根据当前 DMA 目的地址与缓冲区起始地址的差值计算已接收字节数,
* 结果存入 comState.RxLenth 中。
* @example Get_UART_Rx_ActualLength();
**/
void Get_UART_Rx_ActualLength(void)
{
/* 缓冲区数据长度计算,获取当前帧长度*/
comState.RxLenth = LHL_DMA_GetDestAddress(DMA_CHANNEL_UART_Rx) - (u32)&comState.RxdData ;
}
//发送--------------------------------------------------------------------
/**------------------------------------------------------------------------
* @brief UART DMA 发送初始化
* @note 使用 DMA 通道 UART_Tx 将 comState.TxdData 中的数据发送出去,
* 发送长度由 comState.TxLenth 决定,单次模式。
* 使能发送完成中断,并自动启动 DMA。
* @example DMA_UART_Tx_Init();
**/
void DMA_UART_Tx_Init(void)
{
DMA_DMAMUX_CFG(DMA_CHANNEL_UART_Tx,REQUEST_SOURCE_UART_TX);//将DMAMUX链接到串口接收通道(DMA通道0)
/* 3.4 配置DMA通道1负责缓存区数据搬运到UART0 TX数据 */
DMA_Handle_UartTx.Channel = DMA_CHANNEL_UART_Tx;
DMA_Handle_UartTx.Mode = DMA_DIRECT_MODE;
DMA_Handle_UartTx.Request = DMA_HARDWARE_REQUEST;
DMA_Handle_UartTx.Init.Direction = DMA_MEMORY_TO_PERIPH; // 内存->外设
DMA_Handle_UartTx.Init.Src_Address = (uint32_t)&comState.TxdData; // 源:缓存区
DMA_Handle_UartTx.Init.Dest_Address = (uint32_t)(&DMA_UART->RBR_THR_DLL); // 目的UART TX寄存器
DMA_Handle_UartTx.Init.Data_Width = DMA_DATA_WIDTH_1B;
DMA_Handle_UartTx.Init.Data_Size = 1;
DMA_Handle_UartTx.Init.Repetition = comState.TxLenth ;
DMA_Handle_UartTx.Init.Trans_Mode = DMA_SINGLE_TRANSMISSION; // 单次模式
if (LHL_DMA_Init(&DMA_Handle_UartTx) != LHL_OK)
{
while(1); // DMA Init Error
}
// /* 使能DMA中断 */
LHL_DMA_ITConfig(&DMA_Handle_UartTx, DMA_IT_MAJOR, ENABLE); // 配置DMA1主中断
// NVIC_EnableIRQ(DMA1_CH0_IRQn); //DMA_CHANNEL_UART_Tx
LHL_DMA_Start(&DMA_Handle_UartTx); /* 显式启动DMA0 ,开始发送*/
}