飞思卡尔(三)测速测正反转(K60 欧姆龙500线编码器DMA测速)

转载请注明出处: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);

对要使用的IO口进行初始化之后,便是对其寄存器数据的读取了。
//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);
}


测速度是一大重点,而另外一大重点显然是侧正反转。测正反转是直立车原地平衡中不可或缺的一部分。

 

测正反转的电路和原理如下图:  


      分辨正反转是根据A相和B相上升沿到来的顺序分别得,结合上图和按照自己查到的一些资料,分许一下该图。


       该图的下半部分是电路图,这是钟控触发器,这个触发器有四个引脚,右边两个是互逆的2个输出,D引脚接A相CLK引脚接B相,CLK引脚即为时钟线,当B相输出为上升沿的时候触发,将A相此时的输出给Q端口。当B相输出为其他时,不触发。

      举例: 

a.见上图的上半部分。如果蓝色为A相,黄色为B相,在红色虚线位置时,B相输出上升沿,CLK采集触发器触发。此时A相输出为高,则Q输出为高,因此当单片机检测到Q输出为高时,A相的上升沿先于B相的上升沿到来,所以为正转。

b.如果蓝色为B相,黄色为A相,在红色虚线前半个周期时,B相输出上升沿,CLK采集触发器触发。此时A相输出为低,则Q输出为低,因此当单片机检测到Q输出为低时,A相的上升沿后于B相的上升沿到来,所以反转。


评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值