/** * @file usart0.cpp * @brief USART0 串口模块实现与 printf 重定向。 */ #include "main.h" #include "device/usart0.h" #include "sys/config.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) { __disable_irq(); const uint16_t next = nextIndex(s_txHead); if(next == s_txTail) { __enable_irq(); s_dropCount++; return false; } s_txBuf[s_txHead] = static_cast(ch); s_txHead = next; usart_interrupt_enable(USART0, USART_INT_TBE); __enable_irq(); 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, 2U, 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(); }