您的位置:首页 > 编程语言

STM32F207 USART+DMA代码+个人理解

2016-05-20 14:30 351 查看
环境:STM32F207

目的USART通过DMA通信+DMA初步理解

1.之前发送数据的方式

①数据放到串口数据寄存器里面

②等待一个字节发送完成

③重复第一二步。 

看到我们平时的方式我们就会有个想法,如果我们发送五百个字节,我们就需要让CPU在这里等待五百次。也就是在等待过程中我们是不能够去做其他事情的,只能够通过一个while循环去查看串口状态寄存器里面对应的发送完成标志是否为1。

2.现在我们大致描述一下DMA传输模式:

①数据写好放在内存  

②告诉DMA,我们发送多少数据

③干其他事情

④有想要发送数据了,先看看发送完了没有,没有发送完需要等待

⑤重复①②③④步骤

我们看到其实DMA也需要等,很多同学就说,这不是一样吗,我们都需要等待。其实这个情况不一样,这就像一岁小孩吃饭和十岁小孩吃饭一个道理,一岁小孩吃饭需要我们大人一口一口的喂他,在这期间,我们不能够去干其他事情,只能够看到他,第一口吃完了才能够喂第二口。  但是十岁小孩子吃饭就不一样了,你把饭给弄好,然后给他,让他自己吃,这个期间我们就可以去洗衣服了,如果你还想让小孩子吃完这一碗饭再吃一碗,那么你等一会儿去看看就可以了。

言归正传,如果我们用普通方式去做的话,相当于软件去循环查询,但是DMA的话,相当于硬件来做,到底谁快谁慢,我们就不用说了。

其实说了那么多,就是想给大家普及一下DMA大致的工作方式,到底好不好,还是要看具体的项目,对时间的要求等等。下面我对我的代码进行一个讲解。

总体如下:

void usart1_Init(void)
{
usart1_PortInit(); //相关管脚初始化
usart1_IT_Init(); //中断初始化
usart1_DmaInit(); //DMA初始化

}其中PortInit和IT_Init就不需要详细说明了,和一般模式一样,只不过需要在PortInit里面加一个DMA使能,如下图所示:



这里调用了一个USART_DMACmd去使能对应的DMA功能,现在我们跟踪进去看看,这个函数到底做了什么,对应哪个寄存器哪一位,有什么用:

跟踪进去发现两个东西:



①USART_DMAReq 参数的值是什么,我们进到assert_param看到如下:



②再看看寄存器的说明:



其实关于DMAT,我个人的理解就是,使能了这个,我们就可以让USART控制器明白发送的数据可以从DMA通道过来,不一定要我们人为一个一个给DR寄存器里面放。其实我觉得这样理解也符合情理。

好了,上面将串口使能DMA说了,下面将要说说DMA配置了,这个也是我们这篇文章的重中之重,我个人理解,如果这个理解到了的话,比如AD+DMA什么的都应该能够理解了。

DMA学前普及:

在我们具体把一大堆代码搬上来之前,我们先说说DMA相关知识

1.流和通道的关系,直接上图



初学者看到这里一定会问,为哈先来这个图,因为F1系列和F2系列不同,F1系列只有通道(channel)一说,但是F2多了一个流(Stream),为啥?很显然,因为F2支持的DMA通道更加多了。我们要用USART1_TX,我们就马上能够看出来我们需要定位到Stream7-Channel4. 然后通过一大堆的设置,我们就完成了设置。

2.配置DMA需要想到哪些东西?所以我们应该有以下问题:

①我们使用的外设对应DMA 的StreamX channelX

②我们的外设地址是多少

③我们的内存地址是多少

④我们数据传输方向是什么,是内存->外设还是外设->内存

⑤内存的大小是多少

⑥外设地址是否需要增加,比如ADDR ADDR+1 ADDR+2

⑦内存地址是否需要增加,比如ADDR ADDR+1 ADDR+2

⑧内存和外设分别用的是byte,short,还是Int,也就是位宽是多少

⑨我们的DMA是一直运转还是只需要运转一次就停止

⑩当前DMA请求的等级高度

11.是否使用FIFO

12.如果使用FIFO,当FIFO数据存量为多少的时候将数据发送出去

13.还有就是对内存和外设burst模式的配置,这个地方我也不是很懂,具体的话,可以看看链接点击打开链接

如果能够理解上面我说的词汇的话,DMA设置就应该没多大问题了,下面可以上代码了,我都进行了备注,如下:

void usart1_DmaInit(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;

//使能DMA总线时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
//去初始化STREAM 7 ,USART1_TX流 将使用CHANNEL 4
DMA_DeInit(DMA2_Stream7);

//wait the DMA reset OK
while(DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){}

//Configure DMA Stream
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //通道4
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; //数据寄存器
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)usartBuf; //内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //外设地址
DMA_InitStructure.DMA_BufferSize = 200; //传输数据个数
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_High; //DMA等级高度
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //不使用FIFO
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; //FIFO溢出个数
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //单次突发模式
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //单次突发模式
DMA_Init(DMA2_Stream7,&DMA_InitStructure);

//使能DMA Stream7的传输中断
DMA_ITConfig(DM
4000
A2_Stream7, DMA_IT_TC, ENABLE);

//中断等级NVIC配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);

DMA_Cmd(DMA2_Stream7,DISABLE);
}

上面既然使用了NVIC配置,肯定少不了中断处理函数,其实这个中断函数就是等DMA把数据发送完了就会产生。如下:
extern unsigned char Usart1DmaSendOverFlag;
void DMA2_Stream7_IRQHandler(void)
{

#if 1
if(DMA_GetITStatus(DMA2_Stream7,DMA_IT_TCIF7) == SET){
DMA_ClearITPendingBit(DMA2_Stream7,DMA_IT_TCIF7);
Usart1DmaSendOverFlag = 1;
}
#endif
}

中断函数就不需要怎么说了吧,也很简单,如果有不懂得,请留言。
下面是我们怎么调用DMA来发送:

void msgOut(unsigned short len)
{

volatile int count = 0;
if(len > 200)
return;

DMA_SetCurrDataCounter(DMA2_Stream7,len);
DMA_Cmd(DMA2_Stream7 , ENABLE);
}

//输出
void strout(char *p)
{
int len = 0;
len = strlen(p);
msgOut(len);
}

void printk(char *msg,...)
{
va_list argp;

while(!Usart1DmaSendOverFlag);//等待上次数据发送完
Usart1DmaSendOverFlag = 0;
va_start(argp,msg);
vsnprintf((char*)usartBuf , sizeof(usartBuf) , msg , argp);
va_end(argp);
strout((char*)usartBuf);
}


其实,我们调用DMA也就是直接发送请求,然后就OK了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  stm32