转载请注明出处:http://blog.csdn.net/lxk7280
最近两天整天朦朦胧胧的,也不知道做了些什么,时间真的是在不自觉中就溜走了,只是早上起来一个贪玩的念头,一天就会没了,甚至只是晚上一个想看一部电影的念头,就会导致第二天一整天没精神做事。自制力是一个人最重要的品质之一,一个念头的影响是很大的。昨天在CSDN上看到July大牛的一句话:"一个有毅力有内心驱动力的人,一旦认定该做什么事之后,便会不顾规则,且义无反顾,不顾一切的去干。" - 引以为戒。
下面说说我在使用DMA模块对编码器测速的经验吧:
第一件要注意的事情是在安装编码器的时候一定要按照该图,在信号线接上上拉电阻。
编码器的引脚图和上拉电阻的连接方式如图:
对于不接上拉电阻的后果,我表示经历过了。下面给出同样正确的程序,在两种情况下,K60采集并发回的数据:
上拉电阻对编码器信号造成什么影响?
不加上拉电阻时,编码器的信号线输出是小尖波,而加上上拉电阻后,编码器的信号输出为方波。前者K60无法检测脉冲,更不谈测速度一说了。
第二件事就是测速的编程,要变成首先要明白测速的方法要用到哪个模块。测速的方法有很多种,如:在PIT定时中读取LPTM脉冲计数(缺点是无法测两路编码器)、FTM正交解码、DMA测速等。
LPTM的缺点是有两个引脚,但是同时只有一路能使用,因此无法测两路编码器。如图:
我使用的是DMA测速,DMA的优点不言而喻。做过摄像头车的人绝对知道DMA的重要性。在智能车竞赛中,DMA可以采集图像,也可以用测速。
关于DMA测速:
众所周知,DMA在智能车竞赛中的运用是极其广泛和实用的,学会并合理的利用DMA,无疑在比赛中会是一个助力。
简单地说,DMA是直接内存读取(Direct Memory Access),是计算机科学中的一种内存访问技术,DMA模块可以不占用CPU资源传输数据,可以大大提高程序运行速度,从而在智能车竞赛中提高车子对赛道的反应能力。DMA的运用不仅仅局限于智能车竞赛,在很多地方都是有益无害的。
为什么DMA能够不需要CPU的介入传输数据?
在STM32中,DMA单元和Cortex CPU之间对总线使用一种交叉存取的机制,DMA传输遵循相应的传输流程。其中,在数据从内存传输到内存的情况下,每传输一个字要消耗5个时钟周期:1个读周期,1个写周期,插入3个空闲周期归CPU使用。所以,每个DMA通道都只是在总线存取周期才会占用总线,即使传输大量的数据,DMA单元最大也只会在消耗40%的数据总线带宽。
所以说STM32中的DMA与CPU对总线的使用方式是交叉式的。
在K60中,DMA是通过DMA控制器接管数据和地址总线。如果CPU正在执行命令,DMA控制利用空闲的地址和数据总线完成数据传送,某种程度上说,CPU运算和数据传送是在并行进行的。K60的DMA数据的传送分为主循环和副循环。主循环一次可能需要副循环很多次。每个副循环传送完毕,对应的DMA通道就进入空闲模式,等待下一次DMA请求。当所有的DMA传送完毕,即置为DONE标志,并且可以通过设置选择传送完毕是否触发中断。此外,可以通过相关寄存器的设置,使用Kinectis的DMA模块的主副循环链接功能、散聚模式、副循环映射。对DMA模块的相应的寄存器进行初始化后,即可开启DMA功能。
接下来附上使用DMA测速的程序:
第一个是对DMA的初始化函数,如下:
void DMACNT_Init(DMACNT_InitTypeDef* DMACNT_InitStruct)
{
uint8_t Dummy = 0;
DMA_InitTypeDef DMA_InitStruct1;
GPIO_InitTypeDef GPIO_InitStruct1;
//配置GPIO
GPIO_InitStruct1.GPIO_Pin = DMACNT_InitStruct->GPIO_Pin;
GPIO_InitStruct1.GPIO_InitState = Bit_RESET;
if(DMACNT_MODE_RISING == DMACNT_InitStruct->DMACNT_Mode)
{
GPIO_InitStruct1.GPIO_IRQMode = GPIO_IT_DMA_RISING;
}
else
{
GPIO_InitStruct1.GPIO_IRQMode = GPIO_IT_DMA_FALLING;
}
GPIO_InitStruct1.GPIOx = DMACNT_InitStruct->GPIOx;
GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(&GPIO_InitStruct1);
//配置DMA
DMA_InitStruct1.Channelx = DMACNT_InitStruct->DMA_Chl;
DMA_InitStruct1.DMAAutoClose = DISABLE;
DMA_InitStruct1.EnableState = ENABLE;
DMA_InitStruct1.MinorLoopLength = 30000;
switch((uint32_t)DMACNT_InitStruct->GPIOx)
{
case PTA_BASE: DMA_InitStruct1.PeripheralDMAReq = PORTA_DMAREQ; break;
case PTB_BASE: DMA_InitStruct1.PeripheralDMAReq = PORTB_DMAREQ; break;
case PTC_BASE: DMA_InitStruct1.PeripheralDMAReq = PORTC_DMAREQ; break;
case PTD_BASE: DMA_InitStruct1.PeripheralDMAReq = PORTD_DMAREQ; break;
case PTE_BASE: DMA_InitStruct1.PeripheralDMAReq = PORTE_DMAREQ; break;
default : break;
}
DMA_InitStruct1.TransferBytes = 1;
//配置目的地址传输参数
DMA_InitStruct1.DestBaseAddr = (uint32_t)&Dummy;
DMA_InitStruct1.DestDataSize = DMA_DST_8BIT;
DMA_InitStruct1.DestMajorInc = 0;
DMA_InitStruct1.DestMinorInc = 0;
//配置源地址传输参数
DMA_InitStruct1.SourceBaseAddr = (uint32_t)&Dummy;
DMA_InitStruct1.SourceDataSize = DMA_SRC_8BIT;
DMA_InitStruct1.SourceMajorInc = 0;
DMA_InitStruct1.SourceMinorInc = 0;
DMA_Init(&DMA_InitStruct1);
}
有了初始化函数之后,我们就可以在主程序对药使用到的IO口进行DMA的初始化,因为DMA1通道我用来采集图像的数据,所以我用到的是DMA0和DMA2两个通道。
//C5
DMACNT_InitStruct1.DMACNT_Mode = DMACNT_MODE_FALLING;
DMACNT_InitStruct1.DMA_Chl = DMA_CH0; //使用通道 0
DMACNT_InitStruct1.GPIOx = PTC; //PTC5 DMA1 用来摄像头
DMACNT_InitStruct1.GPIO_Pin = GPIO_Pin_5;
DMACNT_Init(&DMACNT_InitStruct1);
//A19
DMACNT_InitStruct1.DMACNT_Mode = DMACNT_MODE_FALLING;
DMACNT_InitStruct1.DMA_Chl = DMA_CH2; //使用通道 2
DMACNT_InitStruct1.GPIOx = PTA; //PTA17 DMA1 用来摄像头
DMACNT_InitStruct1.GPIO_Pin = GPIO_Pin_17;
DMACNT_Init(&DMACNT_InitStruct1);
//DMA脉冲计数 获得计数值
uint16_t DMACNT_GetValue(uint8_t DMA_Chl)
{
uint16_t DMA_Value = 0;
DMA_Value = DMA_GetCurrDataCounter(DMA_Chl);
DMA_SetCurrDataCounter(DMA_Chl,30000);
return (30000-DMA_Value);
}
最后便是在主程序中的应用.
while(1)
{
Left_Speed = DMACNT_GetValue(DMA_CH0);
Right_Speed = DMACNT_GetValue(DMA_CH2);
UART_printf("Left_Speed is:%d\r\n",Left_Speed);
UART_printf("Right_Speed is:%d\r\n\r\n",Right_Speed);
DelayMs(500);
}