/** ************************************************************ * @file ringbuffer.c * @brief Loop buffer processing * @author Gizwits * @date 2017-07-19 * @version V03030000 * @copyright Gizwits * * @note Gizwits is only for smart hardware * Gizwits Smart Cloud for Smart Products * Links | Value Added | Open | Neutral | Safety | Own | Free | Ecology * www.gizwits.com * ***********************************************************/ #include "ringBuffer.h" #include "common.h" int8_t ICACHE_FLASH_ATTR rbCreate(rb_t* rb) { if(NULL == rb) { return -1; } rb->rbHead = rb->rbBuff; rb->rbTail = rb->rbBuff; return 0; } int8_t ICACHE_FLASH_ATTR rbDelete(rb_t* rb) { if(NULL == rb) { return -1; } rb->rbBuff = NULL; rb->rbHead = NULL; rb->rbTail = NULL; rb->rbCapacity = 0; return 0; } int32_t ICACHE_FLASH_ATTR rbCapacity(rb_t *rb) { if(NULL == rb) { return -1; } return rb->rbCapacity; } int32_t ICACHE_FLASH_ATTR rbCanRead(rb_t *rb) { if(NULL == rb) { return -1; } if (rb->rbHead == rb->rbTail) { return 0; } if (rb->rbHead < rb->rbTail) { return rb->rbTail - rb->rbHead; } return rbCapacity(rb) - (rb->rbHead - rb->rbTail); } int32_t ICACHE_FLASH_ATTR rbCanWrite(rb_t *rb) { if(NULL == rb) { return -1; } return rbCapacity(rb) - rbCanRead(rb); } int32_t ICACHE_FLASH_ATTR rbRead(rb_t *rb, void *data, size_t count) { int32_t copySz = 0; if(NULL == rb) { return -1; } if(NULL == data) { return -1; } if (rb->rbHead < rb->rbTail) { copySz = min(count, rbCanRead(rb)); memcpy(data, rb->rbHead, copySz); rb->rbHead += copySz; return copySz; } else { if (count < rbCapacity(rb)-(rb->rbHead - rb->rbBuff)) { copySz = count; memcpy(data, rb->rbHead, copySz); rb->rbHead += copySz; return copySz; } else { copySz = rbCapacity(rb) - (rb->rbHead - rb->rbBuff); memcpy(data, rb->rbHead, copySz); rb->rbHead = rb->rbBuff; copySz += rbRead(rb, (char*)data+copySz, count-copySz); return copySz; } } } int32_t ICACHE_FLASH_ATTR rbWrite(rb_t *rb, const void *data, size_t count) { int32_t tailAvailSz = 0; if((NULL == rb)||(NULL == data)) { return -1; } if (count >= rbCanWrite(rb)) { return -2; } if (rb->rbHead <= rb->rbTail) { tailAvailSz = rbCapacity(rb) - (rb->rbTail - rb->rbBuff); if (count <= tailAvailSz) { memcpy(rb->rbTail, data, count); rb->rbTail += count; if (rb->rbTail == rb->rbBuff+rbCapacity(rb)) { rb->rbTail = rb->rbBuff; } return count; } else { memcpy(rb->rbTail, data, tailAvailSz); rb->rbTail = rb->rbBuff; return tailAvailSz + rbWrite(rb, (char*)data+tailAvailSz, count-tailAvailSz); } } else { memcpy(rb->rbTail, data, count); rb->rbTail += count; return count; } } /** ************************************************************ * @file ringbuffer.h * @brief Loop buffer processing * @author Gizwits * @date 2017-07-19 * @version V03030000 * @copyright Gizwits * * @note Gizwits is only for smart hardware * Gizwits Smart Cloud for Smart Products * Links | Value Added | Open | Neutral | Safety | Own | Free | Ecology * www.gizwits.com * ***********************************************************/ #ifndef _GIZWITS_RING_BUFFER_H #define _GIZWITS_RING_BUFFER_H #ifdef __cplusplus extern "C" { #endif #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define min(a, b) (a)<(b)?(a):(b) ///< Calculate the minimum value #pragma pack(1) typedef struct { size_t rbCapacity; uint8_t *rbHead; uint8_t *rbTail; uint8_t *rbBuff; }rb_t; #pragma pack() int8_t rbCreate(rb_t* rb); int8_t rbDelete(rb_t* rb); int32_t rbCapacity(rb_t *rb); int32_t rbCanRead(rb_t *rb); int32_t rbCanWrite(rb_t *rb); int32_t rbRead(rb_t *rb, void *data, size_t count); int32_t rbWrite(rb_t *rb, const void *data, size_t count); #ifdef __cplusplus } #endif #endif 1、Serial drive for Module communication,(1、Interrupt receive data to write to the ring buffer ;2、实现uartWrite() Serial send function) 2、Serial print function , printf()
时间: 2025-07-09 19:44:06 浏览: 3
<think>我们有两个任务:1. 实现串口驱动(中断接收数据到环形缓冲区,以及串口发送函数)2. 实现printf重定向。
首先,我们已经有环形缓冲区的实现(ringbuffer.h和ringbuffer.c),我们可以直接使用它。
接下来,我们需要实现串口驱动,包括初始化、中断服务函数(将接收到的数据写入环形缓冲区)以及发送函数。
最后,我们需要重定向printf,使其通过串口发送数据。
步骤:
1. 初始化串口,配置相应的GPIO和USART,并开启接收中断。
2. 在中断服务函数中,将接收到的数据写入环形缓冲区。
3. 实现一个串口发送函数uartWrite(),用于发送数据。
4. 重定向fputc,使得printf能够通过串口输出。
注意:我们使用USART2,但代码可以适配其他串口。
我们将创建一个新的文件(例如usart.c和usart.h)来实现这些功能。
以下是具体代码:</think># 串口驱动与打印功能实现方案
下面我将基于您提供的环形缓冲区代码,实现完整的串口驱动模块,包括中断接收、串口发送和printf重定向功能。
## 完整串口驱动实现
### 头文件 `usart.h`
```c
#ifndef __USART_H__
#define __USART_H__
#include "stm32f10x.h"
#include "ringbuffer.h"
// 串口配置结构体
typedef struct {
USART_TypeDef* USARTx; // USART实例
uint32_t baudrate; // 波特率
GPIO_TypeDef* GPIOx; // GPIO端口
uint16_t tx_pin; // TX引脚
uint16_t rx_pin; // RX引脚
uint8_t irq_priority; // 中断优先级
rb_t rx_buffer; // 接收环形缓冲区
} USART_Config;
// 初始化串口
void USART_Init(USART_Config *config);
// 串口发送数据
int32_t USART_Write(USART_TypeDef* USARTx, const uint8_t *data, uint16_t len);
// 串口接收数据
int32_t USART_Read(USART_TypeDef* USARTx, uint8_t *data, uint16_t len);
// 获取指定串口的接收缓冲区
rb_t* USART_GetRxBuffer(USART_TypeDef* USARTx);
// 重定向printf支持
#ifdef __GNUC__
int _write(int file, char *ptr, int len); // GCC编译器重定向
#else
int fputc(int ch, FILE *f); // Keil/IAR编译器重定向
#endif
#endif // __USART_H__
```
### 实现文件 `usart.c`
```c
#include "usart.h"
#include <stdio.h>
// 串口配置数组(支持多个串口)
#define MAX_USART_INSTANCES 3
static USART_Config usart_configs[MAX_USART_INSTANCES] = {
{USART1, 115200, GPIOA, GPIO_Pin_9, GPIO_Pin_10, 0, {0}},
{USART2, 115200, GPIOA, GPIO_Pin_2, GPIO_Pin_3, 1, {0}},
{USART3, 115200, GPIOB, GPIO_Pin_10, GPIO_Pin_11, 2, {0}}
};
// 获取串口配置
static USART_Config* get_usart_config(USART_TypeDef* USARTx) {
for (int i = 0; i < MAX_USART_INSTANCES; i++) {
if (usart_configs[i].USARTx == USARTx) {
return &usart_configs[i];
}
}
return NULL;
}
/**
* @brief 初始化串口
* @param config 串口配置结构体
*/
void USART_Init(USART_Config *config) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1. 初始化接收环形缓冲区
rbCreate(&config->rx_buffer);
// 2. 使能时钟
if (config->USARTx == USART1) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
} else if (config->USARTx == USART2) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
} else if (config->USARTx == USART3) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
}
// 3. 配置TX引脚为复用推挽输出
GPIO_InitStructure.GPIO_Pin = config->tx_pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(config->GPIOx, &GPIO_InitStructure);
// 4. 配置RX引脚为浮空输入
GPIO_InitStructure.GPIO_Pin = config->rx_pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(config->GPIOx, &GPIO_InitStructure);
// 5. 配置USART参数
USART_InitStructure.USART_BaudRate = config->baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(config->USARTx, &USART_InitStructure);
// 6. 使能接收中断
USART_ITConfig(config->USARTx, USART_IT_RXNE, ENABLE);
// 7. 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = (config->USARTx == USART1) ? USART1_IRQn :
(config->USARTx == USART2) ? USART2_IRQn :
USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = config->irq_priority;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 8. 使能串口
USART_Cmd(config->USARTx, ENABLE);
}
/**
* @brief 串口发送数据
* @param USARTx USART实例
* @param data 要发送的数据
* @param len 数据长度
* @return 实际发送的字节数
*/
int32_t USART_Write(USART_TypeDef* USARTx, const uint8_t *data, uint16_t len) {
for (uint16_t i = 0; i < len; i++) {
// 等待发送寄存器空
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
// 发送数据
USART_SendData(USARTx, data[i]);
}
// 等待发送完成
while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
return len;
}
/**
* @brief 串口接收数据
* @param USARTx USART实例
* @param data 接收数据缓冲区
* @param len 要读取的长度
* @return 实际读取的字节数
*/
int32_t USART_Read(USART_TypeDef* USARTx, uint8_t *data, uint16_t len) {
USART_Config *config = get_usart_config(USARTx);
if (!config) return -1;
return rbRead(&config->rx_buffer, data, len);
}
/**
* @brief 获取指定串口的接收缓冲区
* @param USARTx USART实例
* @return 环形缓冲区指针
*/
rb_t* USART_GetRxBuffer(USART_TypeDef* USARTx) {
USART_Config *config = get_usart_config(USARTx);
return config ? &config->rx_buffer : NULL;
}
// 中断服务函数实现
void USART1_IRQHandler(void) {
USART_IRQHandler(USART1);
}
void USART2_IRQHandler(void) {
USART_IRQHandler(USART2);
}
void USART3_IRQHandler(void) {
USART_IRQHandler(USART3);
}
/**
* @brief 通用串口中断处理
* @param USARTx USART实例
*/
static void USART_IRQHandler(USART_TypeDef* USARTx) {
USART_Config *config = get_usart_config(USARTx);
if (!config) return;
// 接收中断
if (USART_GetITStatus(USARTx, USART_IT_RXNE) != RESET) {
// 读取接收到的数据
uint8_t data = USART_ReceiveData(USARTx);
// 写入环形缓冲区
uint8_t temp[1] = {data};
rbWrite(&config->rx_buffer, temp, 1);
// 清除中断标志
USART_ClearITPendingBit(USARTx, USART_IT_RXNE);
}
}
// printf重定向实现
#ifdef __GNUC__
/* GCC编译器重定向 */
int _write(int file, char *ptr, int len) {
// 默认使用USART1输出
USART_Write(USART1, (uint8_t*)ptr, len);
return len;
}
#else
/* Keil/IAR编译器重定向 */
int fputc(int ch, FILE *f) {
// 默认使用USART1输出
USART_SendData(USART1, (uint8_t)ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
#endif
```
## 功能说明
### 1. 中断接收数据到环形缓冲区
```c
// 中断服务函数
void USART1_IRQHandler(void) {
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
// 读取接收到的数据
uint8_t data = USART_ReceiveData(USART1);
// 写入环形缓冲区
uint8_t temp[1] = {data};
rbWrite(&config->rx_buffer, temp, 1);
// 清除中断标志
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
```
**实现原理:**
1. 当串口接收到数据时,触发USART_IT_RXNE中断
2. 在中断服务函数中读取接收到的字节
3. 将字节写入预先配置好的环形缓冲区
4. 清除中断标志位
**优点:**
- 非阻塞式接收,不占用CPU时间
- 缓冲区机制避免数据丢失
- 支持高波特率通信
### 2. 串口发送函数实现
```c
int32_t USART_Write(USART_TypeDef* USARTx, const uint8_t *data, uint16_t len) {
for (uint16_t i = 0; i < len; i++) {
// 等待发送寄存器空
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
// 发送数据
USART_SendData(USARTx, data[i]);
}
// 等待发送完成
while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
return len;
}
```
**功能特点:**
1. 阻塞式发送,确保数据完整传输
2. 支持任意长度数据发送
3. 检查发送完成标志(TC),确保最后字节发送完成
4. 返回实际发送的字节数
### 3. printf重定向实现
```c
// Keil/IAR编译器重定向
int fputc(int ch, FILE *f) {
USART_SendData(USART1, (uint8_t)ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
// GCC编译器重定向
int _write(int file, char *ptr, int len) {
USART_Write(USART1, (uint8_t*)ptr, len);
return len;
}
```
**实现原理:**
1. 重写标准库的输出函数(fputc或_write)
2. 将字符或字符串发送到指定串口
3. 等待发送完成后再返回
**使用方法:**
```c
#include <stdio.h>
int main() {
// 初始化串口
USART_Config usart1_cfg = {USART1, 115200, GPIOA, GPIO_Pin_9, GPIO_Pin_10, 0};
USART_Init(&usart1_cfg);
// 使用printf输出
printf("系统启动完成\r\n");
printf("当前温度: %.1f℃\r\n", 25.5f);
// 使用串口直接发送
const char *message = "Hello, World!\r\n";
USART_Write(USART1, (uint8_t*)message, strlen(message));
while(1) {
// 主循环
}
}
```
## 环形缓冲区优化建议
针对您提供的环形缓冲区代码,我建议进行以下优化:
### 1. 添加缓冲区初始化函数
```c
/**
* @brief 初始化环形缓冲区
* @param rb 环形缓冲区指针
* @param buffer 数据缓冲区
* @param capacity 缓冲区容量
*/
int8_t rbInit(rb_t* rb, uint8_t *buffer, size_t capacity) {
if (NULL == rb || NULL == buffer) {
return -1;
}
rb->rbBuff = buffer;
rb->rbCapacity = capacity;
rb->rbHead = rb->rbBuff;
rb->rbTail = rb->rbBuff;
return 0;
}
```
### 2. 添加缓冲区状态检查
```c
/**
* @brief 检查缓冲区是否为空
* @param rb 环形缓冲区指针
* @return 1为空,0为非空,-1为错误
*/
int8_t rbIsEmpty(rb_t *rb) {
if (NULL == rb) {
return -1;
}
return (rb->rbHead == rb->rbTail) ? 1 : 0;
}
/**
* @brief 检查缓冲区是否已满
* @param rb 环形缓冲区指针
* @return 1为满,0为非满,-1为错误
*/
int8_t rbIsFull(rb_t *rb) {
if (NULL == rb) {
return -1;
}
uint8_t *next = rb->rbHead + 1;
if (next >= rb->rbBuff + rb->rbCapacity) {
next = rb->rbBuff;
}
return (next == rb->rbTail) ? 1 : 0;
}
```
### 3. 添加DMA支持
```c
/**
* @brief 使用DMA发送数据
* @param rb 环形缓冲区指针
* @param data 要发送的数据
* @param len 数据长度
* @return 实际发送的字节数
*/
int32_t rbWriteDMA(rb_t *rb, const void *data, size_t len) {
// 检查DMA状态
if (DMA_GetFlagStatus(DMA1_FLAG_TC7) == SET) {
// 配置DMA传输
DMA_Cmd(DMA1_Channel7, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel7, len);
DMA1_Channel7->CMAR = (uint32_t)data;
DMA_Cmd(DMA1_Channel7, ENABLE);
USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
return len;
}
return 0; // DMA忙,稍后重试
}
```
## 使用示例
### 初始化串口
```c
int main(void) {
// 初始化USART1
USART_Config usart1_cfg = {
.USARTx = USART1,
.baudrate = 115200,
.GPIOx = GPIOA,
.tx_pin = GPIO_Pin_9,
.rx_pin = GPIO_Pin_10,
.irq_priority = 0
};
USART_Init(&usart1_cfg);
// 初始化USART2
USART_Config usart2_cfg = {
.USARTx = USART2,
.baudrate = 9600,
.GPIOx = GPIOA,
.tx_pin = GPIO_Pin_2,
.rx_pin = GPIO_Pin_3,
.irq_priority = 1
};
USART_Init(&usart2_cfg);
printf("串口初始化完成\r\n");
while(1) {
// 处理接收数据
uint8_t buffer[128];
int32_t len = USART_Read(USART2, buffer, sizeof(buffer));
if (len > 0) {
printf("收到 %d 字节数据: ", len);
for (int i = 0; i < len; i++) {
printf("%02X ", buffer[i]);
}
printf("\r\n");
}
// 发送心跳包
static uint32_t last_send = 0;
if (HAL_GetTick() - last_send > 1000) {
const char *heartbeat = "HEARTBEAT\r\n";
USART_Write(USART2, (uint8_t*)heartbeat, strlen(heartbeat));
last_send = HAL_GetTick();
}
}
}
```
## 性能优化建议
1. **DMA传输**:对于高速通信,使用DMA可以大幅降低CPU占用
2. **双缓冲区**:使用乒乓缓冲区避免数据处理时的数据丢失
3. **零拷贝设计**:直接操作缓冲区指针而非数据拷贝
4. **中断优先级**:合理设置串口中断优先级,避免丢失数据
5. **流控制**:实现硬件流控制(RTS/CTS)防止缓冲区溢出
## 相关问题
阅读全文