这篇文章也是本人做完一个项目后抱着想记录点东西巩固下知识同时又想给后来的技术同伴一点参考的想法写下来的,和本人之前有关中颖芯片的博文意图是一致的。看完本篇文章可以了解stm32芯片如何配置定时器正交编码模式的同时对cubemx也会有一定的了解。这款工具很好用,很方便,希望所有用st芯片的伙伴们都能掌握它的用法,使用一种工具很快能上手,主要是大家要了解标准库和HAL库之间的一些微小机制区别就很容易了,这篇文章不打算讲这些,要不跑题了,有空的话专门写一篇文章,不过大家也可以在网上找到很多资料,废话不多说了下一段进入主题。
这里假设大家都下了cubemx软件,对软件具有一定的熟悉度。用stm32f030系列来为例吧。对于什么叫正交编码就不多说了,想必要看我这篇文章的应该都是直接工程或项目中用到了,具有一定基础,实在不知道的就网上找资料了。这里还是插入两幅时序图,一个代表增,一个代表减。
打开cubemx,新建工程,下面直接上图比较直观;
这里选择定时器1,输入通道是通道1和通道2,注意这里的外部中断并不是用来接编码器的第三跟线(可以用作清零计数器作用),后面会向大家解释。
主要配置清单:
1.不分频;
2.向上计数;
3.选择 Encoder Mode TI1 and TI2;
4.两路通道配置成上升沿检测;
这里我是要用4倍模式的即上升沿和下降沿都要检测到并且计数器加一或减一,但为什么cubemx却配制成上升沿检测,之前我也试过配制成双边模式,但效果不对,当然您知道为什么也烦请一定评论告诉我。
到现在也没讲cubemx怎么用,我相信写程序的对这种工具都很容易上手,稍微点一下就通,也就不花多少笔墨在这上面,不过还是要提醒下,使用cubemx生成的代码一定要写在/*BEGIN*/和/*END*/之间,否则下次重新更改配置生成后自己写的代码会被覆盖,配置好之后保存自动生成工程代码。
接下来讲讲一些编程思想,用到正交编码的地方大部分是用作带方向的计数,至于大家各自项目里计数的含义(或者说得到的脉冲数所代表的含义)各不相同,在我的项目中脉冲数代表距离,大家很快便明白。
上图配置中我配置的是0 - 20000计数。使用函数__HAL_TIM_GET_COUNTER(&htim1)可以实时获取计数值。编码器带方向,在我使用的过程中正转计数器从0计数到20000,反转计数器从20000计数到0.这样的话假设从0开始经过一段时间假设计数值是10000那这10000是经过正转n个20000之后的10000还是反转n个20000的10000,可能你会说设一个更新中断计数。我们设置的是向上计数,怎么判断他是正转的20000还是反转的20000更新中断,正转当转到20000时更新中断产生,翻转当转到0跳到20000也有一个更新中断。这时,大家会想到实时设置两个获取计数器的值,更新中断里判断两个值得情况便知道正反转,理论是可以的,但是当转速过快,或者我干脆就在0和20000附近来回转,那么情况便很糟糕,随时计数可能乱套。这时大家还会想到配制成*对齐模式是否可以,本人配置过,效果不对,可能编码器只能从0计数到预装载值,或者从预装载值计数到0;那这个时候怎么办,__HAL_TIM_SET_COUNTER()这个功能派上用场了,初始值用它将计数器配置为10000(这里以20000为例),每次进更新中断在中断里都设置成这个值,这样就达到了从中点要么向上,要么向下计数的结果。避免稍微移动转盘出现0和20000来回产生中断的情况。至于怎么处理数据,我直接上代码,文字已经写得够多了,大家也看不情愿了,最后的结果是可以很方便的得到不管如何转(一直正转,一直反转,时而正转时而反转)相对于最初真实的距离点(是n个正或反20000倍数加m(零头)个脉冲)。
这是中断里面的处理过程(下面的注释和项目是不一致的,主要为了说明问题):
if(htim == &htim1)
{
if(puse_n.puse_count < 10000) //向下计数 和更新中断产生前的计数值比较便知道是正转产生的更新中断还是反转产生的
{
puse_n.loop_num--; //10000的倍数
}
if(puse_n.puse_count > 10000) //向上计数
{
puse_n.loop_num++;
}
__HAL_TIM_SET_COUNTER(&htim1,10000);
}
这是获取最后结果的函数:
void get_true_distance(Puse_number* my_puse)
{
my_puse->puse_count = __HAL_TIM_GET_COUNTER(&htim1);
if(my_puse->loop_num == 0 && my_puse->puse_count > 10000) //刚开始是正转
{
my_puse->true_puse = my_puse->puse_count - 10000;
}
else if(my_puse->loop_num == 0 && my_puse->puse_count < 10000) //刚开始是反转
{
my_puse->true_puse = 10000 - my_puse->puse_count;
}
else if(my_puse->loop_num > 0 )
{
my_puse->true_puse = my_puse->loop_num*10000 + my_puse->puse_count - 10000; //正转my_puse->loop_num倍的10000后
}
else if(my_puse->loop_num < 0)
{
my_puse->true_puse = (-1)*my_puse->loop_num*10000 + 10000 - my_puse->puse_count; //反转my_puse->loop_num倍的10000后
}
else if(my_puse->loop_num == 0 && my_puse->puse_count == 10000) //起点
{
my_puse->true_puse = 0;
}
}
my_puse->true_puse是最后结果,这里代码里面有一点需要提出,当采用中间值时,实际更新中断是每10000个脉冲计数产生一次,变量命名给阅读者带来的麻烦请谅解。
希望对使用编码器的技术同伴有所启发,不对之处烦请指正!