Files
DCU_APP/GD32E508_DCU_APP/device/usart0.cpp
T
cbb 1612e326ee feat(cproject): 重构项目文件结构并添加EtherCAT通信支持
- 将设备相关文件重新组织为device/spi和comm分组
- 添加EtherCAT从站通信模块(esc_exmc_bus、esc_lan9252、ethercat_slave等)
- 添加EXMC外设组件支持
- 统一文件命名规范为小写

refactor(canfd): 优化CANFD模块中断安全性和配置方式

- 替换自定义临界区函数为RT_CRITICAL_SAVE/RESTORE宏
- 使用配置常量CONFIG_IRQ_PREEMPT_CAN替代硬编码值
- 简化邮箱满时的处理逻辑,移除不必要的SysTick等待
- 移除废弃的中断保存头文件引用

refactor(timer): 更新定时器配置以使用配置常量

- 修复头文件包含路径大小写问题
- 使用CONFIG_NVIC_PRIORITY_GROUP配置项替代硬编码值
2026-06-26 00:57:59 +08:00

162 lines
4.8 KiB
C++

/**
* @file usart0.cpp
* @brief USART0 串口模块实现与 printf 重定向。
*/
#include "main.h"
#include "device/usart0.h"
#include "sys/config.h"
#include "sys/critical.h"
uint8_t Usart0::s_txBuf[Usart0::kTxBufSize];
volatile uint16_t Usart0::s_txHead = 0U;
volatile uint16_t Usart0::s_txTail = 0U;
volatile uint32_t Usart0::s_dropCount = 0U;
/**
* @brief 计算发送环形缓冲区中的下一个索引。
* @param[in] idx 当前索引值。
* @param[out] 无。
* @retval uint16_t 返回环形缓冲区中的下一个索引。
*/
uint16_t Usart0::nextIndex(uint16_t idx)
{
return (idx + 1U) % kTxBufSize;
}
/**
* @brief 判断 USART0 发送缓冲区是否已满。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示发送缓冲区已满,false 表示仍有可写空间。
*/
bool Usart0::isBufferFull()
{
return nextIndex(s_txHead) == s_txTail;
}
/**
* @brief 判断 USART0 发送缓冲区是否为空。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示发送缓冲区为空,false 表示仍有待发数据。
*/
bool Usart0::isBufferEmpty()
{
return s_txHead == s_txTail;
}
/**
* @brief 以非阻塞方式向 USART0 发送缓冲写入一个字符。
* @param[in] ch 待发送字符。
* @param[out] 无。
* @retval bool true 表示成功写入发送缓冲,false 表示缓冲已满而被丢弃。
* @note 当前统一由 TBE 中断从队列中取字节发送;此处只负责入队并使能发送中断。
*/
bool Usart0::putCharNonBlocking(int ch)
{
uint32_t save = RT_CRITICAL_SAVE();
const uint16_t next = nextIndex(s_txHead);
if(next == s_txTail) {
RT_CRITICAL_RESTORE(save);
s_dropCount++;
return false;
}
s_txBuf[s_txHead] = static_cast<uint8_t>(ch);
s_txHead = next;
usart_interrupt_enable(USART0, USART_INT_TBE);
RT_CRITICAL_RESTORE(save);
return true;
}
/**
* @brief 将字符写入 USART0 发送环形缓冲。
* @param[in] ch 待发送字符。
* @param[in] f 标准流指针,实际未使用。
* @param[out] 无。
* @retval int 返回待发送字符值。
* @note 当前采用非阻塞写入策略;若缓冲已满则丢弃该字符但仍返回原字符值。
*/
int Usart0::putChar(int ch, FILE *f)
{
(void)f;
(void)putCharNonBlocking(ch);
return ch;
}
/**
* @brief USART0 中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 处理发送缓冲区的 TBE 中断驱动输出;当缓冲为空时自动关闭发送中断。
*/
void Usart0::onInterrupt()
{
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_TBE) != RESET) {
if(!isBufferEmpty()) {
usart_data_transmit(USART0, s_txBuf[s_txTail]);
s_txTail = nextIndex(s_txTail);
} else {
usart_interrupt_disable(USART0, USART_INT_TBE);
}
}
}
/**
* @brief 配置 USART0 对应 GPIO 引脚。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 将 PA9 配置为复用推挽发送脚,将 PA10 配置为浮空输入接收脚,并使能 AF 时钟。
*/
void Usart0::configurePins()
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
}
/**
* @brief 初始化 USART0 外设与中断。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 使用 `CONFIG_USART0_BAUDRATE` 配置串口参数,并使能 USART0 TBE 中断用于队列发送。
*/
void Usart0::init()
{
rcu_periph_clock_enable(RCU_USART0);
usart_deinit(USART0);
usart_baudrate_set(USART0, CONFIG_USART0_BAUDRATE);
usart_word_length_set(USART0, USART_WL_8BIT);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_enable(USART0);
nvic_irq_enable(USART0_IRQn, CONFIG_IRQ_PREEMPT_SPI_USART, 0U);
}
/* ----- 中断入口 / printf 重定向的 C 包装 --------------------------- */
/**
* @brief 标准库 fputc 重定向入口。
* @param[in] ch 待发送字符。
* @param[in] f 标准流指针。
* @param[out] 无。
* @retval int 返回待发送字符值。
* @note 该 C 包装函数内部转调 `Usart0::putChar()`。
*/
extern "C" int fputc(int ch, FILE *f) { return Usart0::putChar(ch, f); }
/**
* @brief USART0 中断向量入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该 C 包装函数内部转调 `Usart0::onInterrupt()`。
*/
extern "C" void USART0_IRQHandler(void) { Usart0::onInterrupt(); }