项目中常用到串口通信,当需要使用串口中断接不定长数据时,可以参考以下示例:
本实例使用DMA+USART空闲中断来进行不定长数据接受,在数据接收完成后将数据透传。
结果将通过另一个串口信息显示。
1、主函数配置
#include "stm32f10x.h"
#include "printfsupport.h"
#include "usart1.h"
int main(void)
{
//打印信息接口
Printf_Init(115200);
//DMA+USART1接口初始化
USART1_Init(115200);
printf("This is a Usart Test Project\n");
while(1)
{
}
}
主函数中初始化了打印信息串口以及用于数据接收的串口。
2、打印接口配置
C文件如下:
#include "printfsupport.h"
/************************************
*说明:Printf默认重定向为USART3
************************************
*/
/*****************Printf重定向*****************/
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART3->SR&0X40)==0){}//循环发送,直到发送完毕
USART3->DR = (u8) ch;
return ch;
}
/*****************Printf输出串口配置*****************/
/*
配置内容
波特率:BaudRate
数据位:8bit
校验位:无
停止位:1
硬件流控制:无
输出输入模式:发送
*/
void Printf_Init(u32 BaudRate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 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_Tx;
USART_Init(USART3, &USART_InitStructure);
USART_Cmd(USART3, ENABLE);
}
对应的h文件
#ifndef _PRINTF_SUPPORT_H__
#define _PRINTF_SUPPORT_H__
#include "stm32f10x.h"
#include "stdio.h"
void Printf_Init(u32 BaudRate);
#endif
3、DMA+USART配置(以USART1为例)
C原文件如下:
#include "usart1.h"
uint8_t UART1_TX_DATA[USART1_REC_LEN]; //dma源地址 发送
uint8_t UART1_RX_DATA[USART1_REC_LEN]; //缓存地址
uint8_t USART1_RECV_BUFF[USART1_REC_LEN]; //dma目标地址 接收
uint8_t Usart1_Recv_len = 0; //接收数据长度
uint8_t USART1_DMA_TX_Finish = 1; //DMA发送完成
//Usart1初始化
void USART1_Init(uint32_t baud)
{
//结构体
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
//时钟初始化
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//USART1_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化
//USART1_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化
//串口中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //IRQ通道:串口1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
//USART1 DMA TX
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn; //IRQ通道:DMA1通道4
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
//USART1 DMA RX
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn; //IRQ通道:DMA1通道5
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
USART_InitStructure.USART_BaudRate = baud;
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_Tx|USART_Mode_Rx;
//初始化USART端口
USART_Init(USART1, &USART_InitStructure);
//开启USART1中断
USART_ITConfig(USART1,USART_IT_TC,ENABLE);
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
//使能USART1
USART_Cmd(USART1, ENABLE);
//清中断
USART1->SR;
USART1->DR;
//使能DMA传输
USART_DMACmd(USART1, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);
//DMA 串口发送配置
DMA_DeInit(DMA1_Channel4);
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)(&USART1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)UART1_TX_DATA;
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize=USART1_REC_LEN;
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority=DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
DMA_Init(DMA1_Channel4,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel4,DISABLE);
DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
//DMA 串口接收配置
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)(&USART1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)USART1_RECV_BUFF;
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize=USART1_REC_LEN;
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority=DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
DMA_Init(DMA1_Channel5,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel5,DISABLE);
DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
DMA_Cmd(DMA1_Channel5,ENABLE);
}
//Usart1中断
void USART1_IRQHandler(void)
{
//发送字节完成中断
if(USART_GetITStatus(USART1,USART_IT_TC) != RESET)
{
USART_ClearITPendingBit(USART1,USART_IT_TC); //清除标志位
USART1_DMA_TX_Finish = 1;
}
else if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
{
//清除IDLE中断
USART1->SR;
USART1->DR;
USART_ClearITPendingBit(USART1,USART_IT_IDLE);
//读取接收数量
Usart1_Recv_len = USART1_REC_LEN-DMA_GetCurrDataCounter(DMA1_Channel5);
//打印数据
printf("Data:");
for(uint8_t i=0;i<Usart1_Recv_len;i++)
{
UART1_RX_DATA[i]=USART1_RECV_BUFF[i];
printf("%x ",UART1_RX_DATA[i]);
}
printf("\n");
//此时可以透传数据测试
Usart1_DMA_Send(UART1_RX_DATA,Usart1_Recv_len);
//清除DMA接收中断
DMA_ClearITPendingBit(DMA1_IT_TC5);
//重新配置DMA接收数据
DMA_Cmd(DMA1_Channel5,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel5,USART1_REC_LEN);
DMA_Cmd(DMA1_Channel5,ENABLE);
}
}
//RX中断
void DMA1_Channel5_IRQHandler(void)
{
//接收完成DMA中断处理
if(DMA_GetITStatus(DMA1_IT_TC5)==SET)
{
DMA_ClearITPendingBit(DMA1_IT_TC5); //清除中断标志位 (通道5传输完成中断)
}
}
//TX中断
void DMA1_Channel4_IRQHandler(void)
{
//发送完成DMA中断处理
//DMA1_Channel4向USART1数据传输完成 注意USART1并没有实际发送完成,需等待usart1发送完成中断
if(DMA_GetITStatus(DMA1_IT_TC4)==SET)
{
DMA_ClearITPendingBit(DMA1_IT_TC4); //清除中断标志位 (通道4传输完成中断)
DMA_Cmd(DMA1_Channel4, DISABLE); //关闭DMA通道4
}
}
/***************** 发送数组 **********************/
void Usart1_DMA_Send(uint8_t Buffer[],uint16_t len)
{
//发送完成标志检测
if(USART1_DMA_TX_Finish)
{
//发送状态
USART1_DMA_TX_Finish = 0;
//使能DMA将缓冲区数据给串口DR
DMA_Cmd(DMA1_Channel4, DISABLE); //关闭DMA_CHx所指示的通道
DMA1_Channel4 -> CMAR = (u32)Buffer; //设置源地址内存地址切换发给外面的BUFF
DMA_SetCurrDataCounter(DMA1_Channel4,len); //DMA通道的DMA缓存的大小
DMA_Cmd(DMA1_Channel4, ENABLE);
}
}
h文件如下:
#ifndef _USART1_H_
#define _USART1_H_
#include "stm32f10x.h"
#include "stdio.h"
#define USART1_REC_LEN 256
void USART1_Init(uint32_t baud);
void Usart1_DMA_Send(uint8_t Buffer[],uint16_t len);
#endif
4、测试结果
上电:
发送测试:
代码实测完毕,可直接使用。