stm32f103c8t6 串口3通道+引脚PB10 PB11开启DMA并且开启空闲中断,使用STM32F10X_conf.h库函数开发,不使用HAL库,
时间: 2025-06-13 18:26:23 浏览: 13
<think>好的,我现在要帮用户解决在STM32F103C8T6上配置USART3使用PB10和PB11引脚,通过DMA传输和空闲中断进行通信的问题,而且不使用HAL库。首先,我需要回忆一下STM32的标准外设库(StdPeriph库)的使用方法,因为用户要求不依赖HAL库。
首先,配置USART3的引脚。USART3的默认引脚是PB10(TX)和PB11(RX),所以这一步应该没问题。需要开启GPIOB和USART3的时钟,然后设置GPIO的模式为复用推挽输出(TX)和浮空输入(RX)。这里可能需要检查一下复用功能是否正确配置。
接下来是USART3本身的配置。需要设置波特率、数据位、停止位、校验位等。例如,115200波特率,8位数据,1停止位,无校验。这里需要计算USART的BRR寄存器的值,可能要用到STM32的时钟频率,比如如果APB1时钟是36MHz,那么BRR的值应该是36MHz/(16*115200)来计算,或者使用小数部分的分频。
然后是DMA的配置。USART3的发送和接收可能使用不同的DMA通道。例如,USART3_TX可能使用DMA1的Channel2,而USART3_RX可能使用DMA1的Channel3。需要确认具体型号的DMA通道映射是否正确。DMA需要配置传输方向、外设地址(USART3->DR)、内存地址、传输数据长度、循环模式等。对于接收,可能需要设置为从外设到内存,循环模式,以便持续接收数据。发送可能是一次性传输,不需要循环。
接下来是空闲中断的配置。空闲中断在USART的CR1寄存器中有一个IDLEIE位,需要使能。同时,在NVIC中配置USART3的中断优先级并启用中断。在中断服务函数中,当检测到空闲中断时,需要清除IDLE标志位(通过读取USART3->SR和USART3->DR),然后处理接收到的数据,可能涉及到DMA传输长度的计算,比如总长度减去剩余数据长度,然后重置DMA缓冲区。
在初始化流程中,需要按顺序开启时钟、配置GPIO、USART、DMA,然后使能USART和DMA。最后启用空闲中断和全局中断。
需要注意的细节包括:确保所有涉及的时钟(GPIOB、USART3、DMA1)已经开启;DMA的中断是否需要配置(可能不需要,因为空闲中断已经处理);空闲中断的处理是否正确清除标志;DMA缓冲区的地址是否正确,尤其是内存地址是否需要递增而外设地址不递增;检查USART3的中断服务函数的名称是否正确,可能是USART3_IRQHandler。
可能遇到的问题有:DMA传输不启动,可能是DMA配置错误;空闲中断没有触发,可能中断使能位未设置或标志未清除;数据接收不完整,可能是DMA缓冲区大小或循环模式的问题。调试时可以通过检查相关寄存器的标志位,或者使用调试器查看DMA和USART的状态。
用户可能希望实现的是当一帧数据接收完成后(通过空闲中断触发),处理接收到的数据,然后重新启动DMA接收。因此,在空闲中断处理函数中,需要计算接收到的数据长度,保存数据,然后重新配置DMA的缓冲区,以便下一次接收。
最后,测试代码部分需要初始化所有配置,然后可能在一个主循环中处理发送数据,或者通过其他方式触发发送。接收部分则完全由中断和DMA处理,用户需要在空闲中断中处理数据。
需要参考STM32F103的标准外设库手册,确认各个寄存器的配置位和函数的使用方法。例如,使用库函数USART_Init(), DMA_Init()等,或者直接操作寄存器,但用户可能更倾向于使用库函数来提高可读性。
总结步骤:配置GPIO和USART3,配置DMA通道,设置空闲中断,编写中断服务函数处理数据,初始化并使能所有外设,最后测试收发功能。</think>在STM32F103C8T6上配置USART3使用PB10/PB11引脚并通过DMA与空闲中断实现通信(不使用HAL库)的步骤如下:
---
### 一、硬件配置分析
1. **引脚复用**:USART3_TX默认复用PB10,USART3_RX复用PB11[^1]
2. **时钟配置**:需开启GPIOB、USART3、DMA1时钟
3. **DMA通道**:USART3_TX使用DMA1通道2,USART3_RX使用DMA1通道3[^2]
---
### 二、配置步骤
#### 1. GPIO配置
```c
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
// TX配置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// RX配置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOB, &GPIO_InitStruct);
```
#### 2. USART3配置
```c
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART3, &USART_InitStruct);
USART_Cmd(USART3, ENABLE);
```
#### 3. DMA接收配置
```c
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef DMA_InitStruct;
DMA_DeInit(DMA1_Channel3); // RX使用通道3
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(USART3->DR);
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)rx_buffer; // 自定义接收缓冲区
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; // 循环模式
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel3, &DMA_InitStruct);
USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);
DMA_Cmd(DMA1_Channel3, ENABLE);
```
#### 4. 空闲中断配置
```c
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE); // 使能空闲中断
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
```
#### 5. 中断服务函数
```c
void USART3_IRQHandler(void) {
if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) {
USART_ReceiveData(USART3); // 清除空闲中断标志
DMA_Cmd(DMA1_Channel3, DISABLE);
uint16_t recv_len = BUFFER_SIZE - DMA_GetCurrDataCounter(DMA1_Channel3);
DMA_SetCurrDataCounter(DMA1_Channel3, BUFFER_SIZE); // 重置DMA计数器
DMA_Cmd(DMA1_Channel3, ENABLE);
// 处理recv_len长度的数据
}
}
```
---
### 三、关键注意事项
1. **时钟树配置**:确保APB1总线时钟正确(默认36MHz)
2. **DMA缓冲区管理**:使用循环模式时注意缓冲区溢出问题
3. **中断优先级**:DMA中断优先级应高于USART中断[^3]
4. **标志清除**:必须清除USART_SR中的IDLE标志
---
### 四、调试建议
1. 用示波器检查PB10引脚波形
2. 在空闲中断内设置断点观察接收数据长度
3. 使用`USART_SendData()`函数验证基础通信功能
---
阅读全文