霍尔传感器测速 程序 使用树莓派做两轮自平衡车(四)——霍尔编码器测速
使用树莓派做两轮自平衡车(四)——霍尔编码器测速
平衡车只靠MPU6050传感器反馈的位置角度是无法保持静止平衡的,必须结合电机的速度反馈才能实现稳定的自平衡。电机测速一般采用霍尔编码器和光电编码器两种方式。后者的精度要比前者高,价格也贵些。我这里使用的是霍尔编码器。
两个电机有两个编码器,分别接到树莓派的gpio12,gpio16,gpio20和gpio21上。
编码器有两相:A相和B相。它们在波形的输出上相位正好相差90度,我们就是利用这个相位差来判断电机的正反转的。如下图是电机正转的两相波形图。
图中A相领先B相,A上升沿时B都是低电平,B上升沿时A是高电平,A下降沿时B是高电平,B下降沿时A是低电平。也就是说符合上面组合的都表明此时电机是正转。
下图是反转的两相波形图。
该图刚好和上面的相反,这里就不罗嗦了,大家自行分析。
通过AB相配合可以实现4倍频测速,即在每一个跳变沿都计一次数,这样在一个周期内可以有4次计数,这样可以提高测速的精度和速度。当然也可以使用2倍频,即AB分别计数。有些单片机处理速度不够快,可以采用2倍频。
为了提高响应的及时性,我采用GPIO中断来捕获编码器的信号。Linux系统的中断是由内核管理的,在应用层无法使用,所以必须在内核编程。这相当于是编码器的驱动程序,采用内核模块很合适。关于GPIO中断和内核模块的内容可以查看我的另一篇文章《Linux的gpio中断》。
我在树莓派上采用4倍频测速,代码的逻辑跟上面描述的一样,如果判断出是正转就将编码器计数变量加1,反转就减1。
得到编码器的值后需要从内核层传到应用层,这里我创建了一个字符设备来完成这个功能。在应用层我们只需定期打开并读取这个字符设备就能得到编码器的值。关于字符设备的内容可以查看我的另一篇文章:《Linux的字符设备 》
编码器的计数与电机的转速是正比关系,所以我们不需要非得算出电机的具体转速值。这个计数值就可以直接作为PID控制器的速度反馈量。
编码器这块儿涉及到Linux两个重要的内容——中断和字符设备,还需要了解内核模块和内核编程,对于开发者还是有一定要求的,许多小的知识点需要掌握。不过现在有许多关于这些内容的书,网上也有很多资料,相信是难不住大家的。
OK,关于编码器就说到这里吧,希望对大家有所帮助,喜欢的小伙伴就点赞支持一下吧。
限于个人水平,难免有错误和遗漏,欢迎大家交流指正。
项目实战:51单片机霍尔自行车里程测速仪设计项目 原理图 程序
最近后台有小伙伴私信我说,我讲得都太基础了,有没有做过单片机霍尔传感器测速度相关的项目,我现在就把以前做的一个单片机霍尔自行车里程测速仪项目分享给大家。
这个也是我以前做过的一个项目,现在拿出来分享给大家,大家不用觉得难,不过就是硬件做基础,然后用软件代码去控制而已,只要按照教程一步步来做你就会发现其实很简单。
好了,针对此项目下面有几点说明
1、单片机是通用的无论51还是52、无论stc还是at都一样,引脚功能都一样。
程序也是一样的。
2、原理图中的.ddb、.Bkp等格式是要用protel打开的,没有软件的不要紧,
3、程序中的.c文件可以用记事本(文本文档)打开,就是程序了。其他的是写程序是自动生成的,没什么用的。
4、可以按照正面布局,不要按照实物的背面焊接,要按照原理图焊接。
数码管脚位排布说明:
正面朝自己,秒点在下,左下为1脚,逆时针排布,左上为最后一个脚!请坛友焊接前弄清脚位排布再焊接!祝你们成功!
废话不多说,直接上实物图:
霍尔自行车测速电路原理图:
元器件列表
单片机霍尔自行车里程测速仪参考源程序:
//说明1:P00—P07:a-----dp P27—P24:com1-----com4 P34切换显示 P32—INT0 一个磁钢
//说明2:此程序针对车轮周长为207cm,其他型号周长,可改变参数就行
//说明3:数码管从左至右 高------低
#include<reg51.h>
#include"juxun001.h"
//主函数=====================
void main()
{
time0_int0_init();//定时器0和外部中断0的初始化
while(!TR0) //上电一直未切割时就显示 0.0.0.0
{
init_display();
}
while(1)
{
if(!change)//按下切换按键显示里程
{
delay1m(5);
if(!change)
{
flag = ~flag;
}
while(!change);
}
}
}
//==定时器0中断函数
void time0_interrupt()interrupt 1
{
TL0 = (65536 - 5000) % 256;// 12M晶振,5ms定时
TH0 = (65536 - 5000) / 256;
display_function();
time_counter++;
time_counter8++;
if(time_counter8 >= 1600)//大于8s没切割,车子视为停下了,速度为:0,但里程还记着
{
time_counter8 = 0;
speed = 0;//速度为 0
point1 = 0;
buf1[3] = speed%10;buf1[2] = speed/10%10;
buf1[1] = speed/100%10;buf1[0] = speed/1000%10;
}
}
//==========外部中断0中断函数=
void int0_interrupt()interrupt 0
{
external_counter++;
if(external_counter ==1 )TR0 = ~TR0;//第一次切割打开定时器0
if(external_counter == 65535)external_counter = 0;
time_counter8 = 0; //只要8秒内有切割,车子就仍在运行
if(external_counter % 2 == 0)//切割2次 更新下速度
{ //*5是因为中断一次是5MS
if(flag10){speed =((ulong)(36000)*zhouchang)/(time_counter * 5);flag1=1;}
else if(flag11){speed =((ulong)(36000)2zhouchang)/(time_counter * 5);}//速度:单位为 m/h ,*1000的目的是不出现浮点数
if((speed >= 100)&&(speed < 1000)) //100 ---- 1000 3位整数
{
point1 = 1;buf1[3] = speed%10;buf1[2] = speed/10%10;
buf1[1] = speed/100%10;buf1[0] = 0;}
else if((speed >= 1000)&&(speed < 10000)) //1000 ---- 10000 4位整数
{
point1 = 1;speed = (uint)speed;
buf1[3] = speed%10;buf1[2] = speed/10%10;
buf1[1] = speed/100%10;buf1[0] = speed/1000%10;}
else if((speed >= 10000)&&(speed < 100000)) //10000 ----- 100000 5位整数
{
point1 = 2;
buf1[3] = speed/10%10;buf1[2] = speed/100%10;
buf1[1] = speed/1000%10;buf1[0] = speed/10000%10;}
else if((speed >= 100000)&&(speed < 1000000)) //100000 ------ 1000000 6位整数
{
point1 = 3;
buf1[3] = speed/100%10;buf1[2] = speed/1000%10;
buf1[1] = speed/10000%10;buf1[0] = speed/100000%10;}
time_counter = 0;
}
if(external_counter % 8 == 0)//每切割8次 更新下里程
{
s = (ulong)external_counter * zhouchang; //里程:单位为 cm
if((s >= 1000)&&(s < 10000))
{
point2 = 1; // 0.012 001212
buf2[3] = s/100%10;buf2[2] = s/1000%10;
buf2[1] = 0;buf2[0] = 0;}// 4位整数
else if((s >= 10000)&&(s < 100000))
{
point2 = 1;
buf2[3] = s/100%10;buf2[2] = s/1000%10;
buf2[1] = s/10000%10;buf2[0] = 0;}// 5位整数 0.123 012345
else if((s >= 100000)&&(s < 1000000))
{
point2 = 1;
buf2[3] = s/100%10;buf2[2] = s/1000%10;
buf2[1] = s/10000%10;buf2[0] = s/100000%10;} // 6位整数 1.234 1234 56
else if((s >= 1000000)&&(s < 10000000))
{
point2 = 2;
buf2[3] = s/1000%10;buf2[2] = s/10000%10;
buf2[1] = s/100000%10;buf2[0] = s/1000000%10;}// 7位整数 1234 567
else if((s >= 10000000)&&(s < 100000000))
{
point2 = 3;
buf2[3] = s/10000%10;buf2[2] = s/100000%10;
buf2[1] = s/1000000%10;buf2[0] = s/10000000%10;} // 8位整数 1234 5678
}
}
//==========开电源就显示的数据,初始显示速度
void init_display()
{
uchar i;
for(i = 0;i < 4;i++)
{
wei = bitcode[num - 1];
led = display1[buf1[num–]];
delay1m(4);
if(num == 0)num = 4;
}
}
//==显示函数=
void display_function()
{
if(flag == 0)//显示速度
{
switch(point1)
{
case 0:wei = bitcode[num-1];led = display1[buf1[num-1]];num–;break;//速度显示 0
case 1:if(num == 1){ wei = bitcode[num-1];led = 0x7f&display1[buf1[num-1]];num–;}
else { wei = bitcode[num-1];led = display1[buf1[num-1]];num–;}break;//最高位小数点亮
case 2:if(num == 2){ wei = bitcode[num-1];led = 0x7f&display1[buf1[num-1]];num–;}
else { wei = bitcode[num-1];led = display1[buf1[num-1]];num–;}break;//第二高位小数点亮
case 3:if(num == 3){ wei = bitcode[num-1];led = 0x7f&display1[buf1[num-1]];num–;}
else { wei = bitcode[num-1];led = display1[buf1[num-1]];num–;}break;//第三高位小数点亮
default:break;
}
if(num == 0)num = 4;
}
else if(flag == 1)//显示里程
{
switch(point2)
{
case 0:wei = bitcode[num-1];led = display1[buf2[num-1]];num–;break;//里程显示 0
case 1:if(num == 1){ wei = bitcode[num-1];led = 0x7f&display1[buf2[num-1]];num–;}
else { wei = bitcode[num-1];led = display1[buf2[num-1]];num–;}break;//最高位小数点亮
case 2:if(num == 2){ wei = bitcode[num-1];led = 0x7f&display1[buf2[num-1]];num–;}
else { wei = bitcode[num-1];led = display1[buf2[num-1]];num–;}break;//第二高位小数点亮
case 3:if(num == 3){ wei = bitcode[num-1];led = 0x7f&display1[buf2[num-1]];num–;}
else { wei = bitcode[num-1];led = display1[buf2[num-1]];num–;}break;//第三高位小数点亮
default:break;
}
if(num == 0)num = 4;
}
}
//=================定时器0和外部中断0的初始化函数=
void time0_int0_init()
{
TMOD |= 0x01;
TMOD &= 0xfd;//定时器0工作于方式1
TL0 = (65536 - 5000) % 256;//12M晶振,5ms定时
TH0 = (65536 - 5000) / 256;
IT0 = 1;//外部中断0,负跳变触发方式
TR0 = 0;
ET0 = 1;
EX0 = 1;
EA = 1;
}
//==========ms 级延时函数=
void delay1m(uchar x)
{
uchar i,j;
for(i=0;i<x;i++) //连数x次,约 x ms
for(j=0;j<120;j++); //数120 次,约1 ms
}
好了,以上就是这个项目的全部内容了。
最后,如果有什么意见或者建议欢迎您留言给我,让我们共同学习一起进步,鉴于无法上传附件,如果需要完整代码或设计文件,请留言或私信我,看到后会第一时间回复。
谢谢!
相关问答
霍尔传感器测速 公式?霍尔传感器是一种能够测量磁场的传感器,它可以被用于旋转物体的测速。以下是利用霍尔传感器测速的公式:速度V=S×f其中,S表示旋转物体在某一段时间内的...
3144 霍尔传感器 测量方法?位移测量:两块永久磁铁同极性相对放置,将线性型霍尔传感器置于中间,其磁感应强度为零,这个点可作为位移的零点,当霍尔传感器在Z轴上作△Z位移时,传感器有...
光电测速实验和 霍尔测速 实验谁转速更快?光电测速实验和霍尔测速实验的转速测量速度并无直接可比性,因为它们的速度取决于实际应用场景和设备性能。在理想条件下,光电测速和霍尔测速的转速测量速度都...
霍尔传感器测速 时能否只用一只磁钢?楼上说的霍尔测速,估计是测转速。霍尔传感器一般由霍尔元件和磁钢组成。当霍尔元件和磁钢相对运动时就会产生信号脉冲。根据磁钢和脉冲数量就可以计算转速。转...
电动车 霍尔 线 测速 接哪根?电动车仪表脉速显测速代表脉速,脉速线是跟控制器相接的,跟霍尔没什么关系要说这个脉速怎么来的还是说到控制器,控制器相接电机、霍尔,之后控制器处理,...
霍尔 怎么检测呢?2.1观察输出信号从输出信号可以看出霍尔元件是否正常工作,输出信号需要满足一定的要求才是有效的。2.2测试霍尔元件的特性曲线测试霍尔元件的特性曲线可...
曲轴位置 传感器 三线万用表怎么测?如何使用万用表检测曲轴位...[回答]电磁式的曲轴位置传感器的两根线是信号线,可以用万用表可以测出来,测量电阻应该在1千欧姆左右。启动时候量电压应该在300毫伏以上。扩展资料电磁式...
lg洗衣机电机 霍尔 怎么测量?2、检测控制器绕组线参数:A、用黑表笔接电源正极,用红表笔分别接触黄、绿、兰三根绕阻线,参数在400-700之间B、重复2的步骤3、霍尔信号线检测:用黑表...
高斯仪使用说明?1.将4节电池装入后面板,或接通外接适配电源,将霍尔传感器链接测试主机后进行开机。2长按键盘FUNC进入菜单内选择测试磁场类型静态直流DC磁场或动太AC磁场,选...
如何利用 霍尔 效应测试半导体类型_作业帮[最佳回答]利用霍尔效应可以测量半导体到底是n,p型的把载有电流的半导体放在垂直于电流方向的磁场中时,半导体会产生横向磁场的效应.具体仪器很多,你可以查一...