以前发过利用DMA接收不定长数据的方法,最近把发送也调试好了,这个程序应用多年,一直很好用,适应各种设备,现把程序再整合一下发上来。
主要原理:
1、串口接收:在内存中开启一个缓冲区,DMA指针指向缓冲区首地址,开启接收,并定时判断是否有数据接收到,如果接收到,则继续等待,如果无数据超过规定时间,则认为本帧数据结束,将数据拷贝至其他部位,同时进行下次接收;
2、串口发送:DMA工作与Normal模式,需要发送时,设置DMA指针到发送数组首地址,DMA长度设置为要发送的数据长度,启动DMA后即可开始发送。
优点:
1、无需任何中断,只需要定时调用串口接收函数;
2、收发占用CPU时间极少,经测试,接收和发送函数平时占用时间大约3-10us(48M主频)
3、串口超时时间随意设定,适应多种应用
下面是程序(本程序是在STM32F373上调试的,其他CPU只要改下寄存器名即可使用):
首先是USART.h
#ifndef __USART_H
#define __USART_H
#define UART2_TimeoutComp 5 //串口超时时间2*5=10ms
#define USART_BufferSize2 100 //接收缓冲区大小
#define SRC_USART2_RDR (&(USART2->RDR)) //串口接收寄存器
#define SRC_USART2_TDR (&(USART2->TDR)) //串口发送寄存器
extern u8 USART2_Data[USART_BufferSize2];
void USART_Configuration(void);
void USART_Write(u8* dat,u16 len);
u16 USART_Read(void);
#endif
接下来是USART.c
#include "stm32_includes.h"
#include "USART.h"
//串口接收DMA
DMA_InitTypeDef DMA_InitStructure_Rx;
//串口发送DMA
DMA_InitTypeDef DMA_InitStructure_Tx;
//串口2接收到的数据
u8 USART2_Data[USART_BufferSize2];
//串口2超时
u8 UART2_Timeout;
//串口2接收缓存
u8 uart2_data_temp[USART_BufferSize2];
//串口2上次接收指针位置
u16 uart2_Counter_Last=0;
/////////////////////////////////////////////////////////发送接收程序/////////////////////////////////////////////////////////////////////
//发送多个字节数据
void USART_Write(u8* dat,u16 len)
{
DMA_Cmd(DMA1_Channel7, DISABLE); //关闭DMA
DMA_InitStructure_Tx.DMA_MemoryBaseAddr = (u32)dat; //目标BUF
DMA_InitStructure_Tx.DMA_BufferSize = len; //DMA缓存的大小
DMA_Init(DMA1_Channel7, &DMA_InitStructure_Tx); //根据DMA_InitStruct中指定的参数初始化DMA的通道7寄存器
DMA_Cmd(DMA1_Channel7, ENABLE); //开启DMA
}
//重置串口接收DMA
void USART_ResetDMA_Rx(void)
{
DMA_Cmd(DMA1_Channel6, DISABLE); //关闭DMA
DMA_ClearFlag(DMA1_FLAG_TC6); //清除标志
DMA1_Channel6->CNDTR = USART_BufferSize2; //重置DMA指针
DMA_Cmd(DMA1_Channel6, ENABLE); //开启DMA
UART2_Timeout=0; //清零超时时间
uart2_Counter_Last = USART_BufferSize2; //重置上次接收指针
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//DMA初始化
void USART_DMA_Init(void)
{
//串口接收DMA初始化
DMA_DeInit(DMA1_Channel6); //将DMA的通道6寄存器重设为缺省值
DMA_InitStructure_Rx.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作源头//外设是作为数据传输的目的地还是来源
DMA_InitStructure_Rx.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不递增
DMA_InitStructure_Rx.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure_Rx.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设字节为单位
DMA_InitStructure_Rx.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //内存字节为单位
DMA_InitStructure_Rx.DMA_Mode = DMA_Mode_Circular; //工作在循环模式
DMA_InitStructure_Rx.DMA_Priority = DMA_Priority_High;
DMA_InitStructure_Rx.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure_Rx.DMA_PeripheralBaseAddr = (u32)SRC_USART2_RDR; //源头寄存器
DMA_InitStructure_Rx.DMA_MemoryBaseAddr = (u32)uart2_data_temp; //目标BUF
DMA_InitStructure_Rx.DMA_BufferSize = USART_BufferSize2; //DMA缓存的大小
DMA_Init(DMA1_Channel6, &DMA_InitStructure_Rx); //初始化DMA的通道7寄存器
DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, DISABLE); //关闭DMA6传输完成中断
USART_DMACmd(USART2,USART_DMAReq_Rx,ENABLE); //使能USART2的接收DMA请求
DMA_Cmd(DMA1_Channel6, ENABLE); //正式允许DMA
//串口发送DMA初始化
DMA_DeInit(DMA1_Channel7); //将DMA的通道7寄存器重设为缺省值
DMA_InitStructure_Tx.DMA_DIR = DMA_DIR_PeripheralDST; //外设作目的地
DMA_InitStructure_Tx.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不递增
DMA_InitStructure_Tx.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure_Tx.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设字节为单位
DMA_InitStructure_Tx.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //内存字节为单位
DMA_InitStructure_Tx.DMA_Mode = DMA_Mode_Normal; //工作在单次模式
DMA_InitStructure_Tx.DMA_Priority = DMA_Priority_High;
DMA_InitStructure_Tx.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure_Tx.DMA_PeripheralBaseAddr = (u32)SRC_USART2_TDR; //源头BUF
DMA_InitStructure_Tx.DMA_MemoryBaseAddr = (u32)uart2_data_temp; //目标BUF
DMA_InitStructure_Tx.DMA_BufferSize = USART_BufferSize2; //DMA缓存的大小
DMA_Init(DMA1_Channel7, &DMA_InitStructure_Tx); //初始化DMA的通道7寄存器
DMA_ITConfig(DMA1_Channel7, DMA_IT_TC, DISABLE); //关闭DMA7传输完成中断
USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE); //使能USART2的发送DMA请求
}
//串口初始化
void USART_Configuration(void)
{
//串口初始化数据结构定义
USART_InitTypeDef USART_InitStructure;
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_InitStructure.USART_BaudRate = 115200;
USART_Init(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
USART_DMA_Init();
}
//串口接收读取程序
u16 USART_Read(void)
{
u16 counter,ret = 0 ;
//超时时间
UART2_Timeout++;
//获取接收指针位置
counter = DMA_GetCurrDataCounter(DMA1_Channel6);
//指针发生变化,说明有数据接收到
if(counter != uart2_Counter_Last) //未完成传输
{
UART2_Timeout = 0; //清零超时时间
uart2_Counter_Last = counter; //保存本次指针位置
}
else //指针没有变化,说明没有数据接收到
{
if(UART2_Timeout >= UART2_TimeoutComp) //产生超时
{
if(uart2_Counter_Last < USART_BufferSize2) //有数据接收到
{
//取得接收到的数据数量
ret = USART_BufferSize2 - uart2_Counter_Last;
//将接收到的数据复制到接收数组中
CopyData(uart2_data_temp,USART2_Data,ret);
//重置串口接收DMA,继续下次接收
USART_ResetDMA_Rx();
}
//清零超时时间
UART2_Timeout=0;
}
}
return ret;
}
接下来是主程序
//--------------------------------------------------------------------------------------------------------------
//数据接收程序
void USART_Task(void)
{
len = USART_Read();
if(len > 0)
{
//分析数据
AnalizeData(USART2_Data, len);
}
return;
}
int main()
{
while(1)
{
USART_Task(); //调用接收程序
delay_ms(2); //2ms调用一次
}
}
发送数据时,只需要调用发送函数USART_Write即可,数据长度随意。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
用定时器定时,不断查询,但不加中断,这样不会打断任务执行,执行完成后再切换任务,任务执行周期会有一定偏差,但都在接受范围,几十us误差。
一周热门 更多>