• 基于STM32单片机的简单红外循迹的实现


    此次红外循迹是我在做毕设过程无意中实现的,所有有些地方不够精确完美,还请各位友友们多多指点校正。这篇博客也是小弟第一篇博客,

    小弟不才,文笔不怎么行,可能有些语句不太通顺的地方,只能让各位将就一下了。那么接下来就进入正题吧。

    一、硬件选择

    首先我们需要一个单片机开发板,在这里我使用的是STM32F103RCT6型号的单片机,这个大家也可以自己买其他的类型;然后就是电机和电机

    驱动模块,市面上的电机有很多种,有带编码器的,也有不带编码器的,由于此次循迹只是简单的巡线操作,所以我用的是不带编码器的直流减速

    电机,大部分的小车其实都够用了,而驱动模块选的是L298N模块,这个应该大家还是比较熟悉吧,后续我也会详细介绍的;然后电源部分的话,

    你可以选择分开给驱动模块和单片机单独供电,也可以用一个电源就行了,我在这里用的两节18650的锂电池给驱动模块供电,然后用了一个移动

    电源给单片机供电;循迹模块我用的是红外传感器,这个其实也有很多类型的,有数字信号的,也有模拟信号的,我用的是简单的输出数字信号

    的TCRT5000传感器,这个在某宝上也便宜,大家可以根据自己需要自行选择。这差不多就是全部硬件了,后面我会分享给大家如何去实现红外循迹

    的具体实现过程。

    二、循迹原理介绍

    其实红外循迹还是比较简单的,当然这里我指的是我自己实现的功能,本次循迹我只用了两路循迹模块,所以你们在视频中看到的循迹效果可能不是

    稳定,但是至少还是没有出现什么意外,小车成功的在轨道上跑动起来了。红外传感器的工作原理是这样的,它上面有两个管子,一个发射管,用

    来向外发射红外线,还有一个接收管,用来接收反射回来的红外线,这个TCRT5000的有效范围好像只有8mm到30mm,根据它的检测距离以及它对不同

    颜色的感应程度,就可以判断地面上的轨迹了;如果地面上是黑色,由于红外线会被黑色吸收,导致发出的光不能被反射回来,接收管就收不到信号,

    它的数字输出引脚D0就会变为高电平,并且模块上的信号灯也处于熄灭状态,这个时候我们就知道小车已经到黑线上了,就可以让小车转弯;而当地

    面上是白色的时候,红外线发出后会被反射回来,接收到反射信号后,模块的数字输出信号就会变为低电平,这个时候模块上的信号灯就会亮起来,

    我们就让小车直走就行了。这就是红外循迹的整个原理,还是容易理解的,至于具体接线情况,我在这里就不多啰嗦了,相信大家对单片机和模块的

    接线还是可以做到的,下面我们就可以进行程序的编写了。

    三、循迹程序介绍

    我在这里就直接给你们上循迹的程序吧,里面有注释应该也差不多看得明白,我把电机转动程序也放在下面了,方便有些同学们可以更好的理解。

    哦,对了,我用的是STM32库函数版本写的程序,如果有用51单片机或者想用寄存器进行编程的朋友们也可以把相关思路转换一下就行了。

    /***循迹模块初始化程序***/

    GPIO_InitTypeDef initstruct;  //这是定义一个GPIO的结构体,用于保存循迹模块的引脚信息
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);  //开启GPIO的时钟,这一步很关键,可以理解为一个开关
    initstruct.GPIO_Pin = GPIO_Pin_0;  //这里我用到的是PB0引脚,你们可以对照自己的引脚改一下
    initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //一般情况下接收引脚的信号选择浮空输入模式就行了,你们也可以试着用上拉或者下拉模式
    GPIO_Init(GPIOB,&initstruct);  //这是对刚才你天的信息进行初始化保存的一个函数
    initstruct.GPIO_Pin = GPIO_Pin_7;  //同理
    initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //同理
    GPIO_Init(GPIOA,&initstruct);  //同理

     

    /***小车开始循迹的程序***/

    #define XJLEFT GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)  //这两个是我在头文件里面的宏定义,其实也可以不用只有写,我只是为了后面方便管理
    #define XJRIGHT GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)

     

    XJleft = XJLEFT;   //这是我定义的两个变量,用来记录循迹模块输出引脚上的电平值   

    XJright = XJRIGHT;

    if((XJleft==0) && (XJright==0))      //这种情况就是小车两个循迹模块都没有检测到黑线,所以我让小车直走
    car_go(60);
    if((XJleft==0) && (XJright==1))    //这种情况是右边循迹模块感应到黑线,也就是要向右边拐弯了
    {
    car_right_two(80);  //这其实就是一个向右转弯的函数,你们可以根据自己的情况来写
    delay_ms(70);    //我在这里加一点延时,是因为我之前测试的时候小车有点卡顿的感觉,加点延时就会好一些
    if((XJleft==0) && (XJright==0))  //这里再嵌套一个 if 判断,是为了让小车回转过后可以继续向前跑,让它更稳定一点,也是为了防止小车乱晃
    car_go(60);
    }
    if((XJleft==1) && (XJright==0))  //这个情况是左边的循迹模块感应到黑线,也就是要向左边拐弯了
    {
    car_left_two(80);    //后面的其实和上面原理差不多
    delay_ms(70);
    if((XJleft==0) && (XJright==0))
    car_go(60);
    }
    if((XJleft==1) && (XJright==1))  //这种情况是左右两个循迹模块都感应到黑线,不过这个没有运用到巡线操作中,是我用来把小车拿起来让它停止的
    car_stop();

     

    这下面就是小车电机转动的程序了,我就把PWM控制电机速度的部分拿来了,具体的原理就不再介绍了,大家对着程序参考一下应该也能理解,

    其实就是一些固定的格式于步骤。

    /***电机PWM初始化程序***/

    TIM_TimeBaseInitTypeDef time4initstruct;  //这和前面GPIO的一样,也是定义一个结构体,用来保存定时器的信息
    TIM_OCInitTypeDef ocinitstruct;      //定义一个结构体,用来保存定时器通道的信息
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);    //开启定时器时钟  (其实开启时钟就是一个开关,因为单片机设置了默认把设备关掉来节省电源
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);   //开启GPIO的时钟     所以如果我们要使用定时器,GPIO端口或者像串口等一些其他的外设的时候

                                               //就要先把这个时钟开关打开,这样设备才能正常工作)

    time4initstruct.TIM_Period = 200;    //这里可能你们有疑惑,其实这个是设置定时器的重装载值
    time4initstruct.TIM_Prescaler = 7200-1;   //这里是设置定时器的预分频系数

                       //以上两个参数联合起来使用,是用来设置定时器的计时周期的,比如重装载值我设置为200,预分频系数我设置为7200,至于为什么

                       //这里要减一,可以百度搜索一下,我在这就不过多赘述了;由于STM32单片机的时钟频率是72MHz,分频系数为7200,那么分频后,

                       //时钟频率就是10KHz,那么计时一个数的时间就是10KHz分之1秒,也就是十分之一毫秒,而重装载值我设置的是200,那么整个定时

                       //的周期就是20ms,整个定时器频率就是50Hz,至于为什么我要设置为50Hz,因为网上说的好像是PWM波控制电机差不多要这个频率
    time4initstruct.TIM_CounterMode = TIM_CounterMode_Up;  //这里是配置计数模式,有向上模式,向下模式,向上向下模式,这里我选择向上计数
    time4initstruct.TIM_ClockDivision = TIM_CKD_DIV1;     //这里是设置时钟分频因子,这个参数我没怎么用过,这里设置为不分频就行了
    TIM_TimeBaseInit(TIM4,&time4initstruct);          //然后把配置好的信息进行初始化保存
    ocinitstruct.TIM_OCMode = TIM_OCMode_PWM1;      //这里是配置定时器通道的模式,即PWM波产生模式,一般有PWM1、PWM2两种,这里我选择PWM1,两种

                                 //模式具体什么含义可以自行百度搜一下
    ocinitstruct.TIM_OCPolarity = TIM_OCPolarity_High;      //这是设置通道的有效电平极性,这里设置为高电平有效
    ocinitstruct.TIM_OutputState = TIM_OutputState_Enable;    //这是一个使能开关,即开启通道使能,让其可以产生PWM波
    TIM_OC3Init(TIM4,&ocinitstruct);  //把配置好的信息进行初始化保存
    TIM_OC4Init(TIM4,&ocinitstruct);  //同理
    TIM_Cmd(TIM4,ENABLE);    //使能定时器,让其开始工作
    TIM_OC3PreloadConfig(TIM4,TIM_OCPreload_Enable);  //至于这后面的三个函数是开启定时器和通道上的预装载使能位,说白了就是让定时器可以重新加载计时
    TIM_OC4PreloadConfig(TIM4,TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM4,ENABLE);

     

    /***电机速度控制程序***/

    这里我只列出一个小车前进的程序,至于小车后退,转弯原理是一样的,大家可以参照根据自己情况进行修改

    GPIO_ResetBits(GPIOB,GPIO_Pin_7);  //给左边电机一个低电平,左边电机正转  相反,给它高电平就反转
    GPIO_ResetBits(GPIOA,GPIO_Pin_4);  //给右边电机一个低电平,右边电机正转
    TIM_SetCompare3(TIM4,100);    //设置左边电机的速度,这里的数值100要与前面定时器那里的重装载值要对应上,速度值为0到200范围内,数值越大,电机转动越快
    TIM_SetCompare4(TIM4,100);

    好了,以上就是本偏博客的所有内容了,由于本人初次接触STM32,加上第一次写博客没有太多经验,还有不好的地方望各位伙伴们指点校正,

    以后我也会将更多的好玩有意义的内容分享给大家的,希望小伙伴们喜欢([表情] ω [表情])

    最后小车循迹的视频点击下方链接即可观看:

    https://www.bilibili.com/video/BV1h34y1x7AR?spm_id_from=333.999.0.0

     

     

     

  • 相关阅读:
    技术分享 | 抓包分析 TCP 协议
    centos7安装redis4.0.10环境以及配置使用
    【从头构筑C#知识体系】2.1 泛型
    Linux输出文件夹下所有文件的完整路径shell脚本
    vue里的变量定义说明
    React 组件进阶
    前端 WebSocket 的一些使用
    STM32 PWM波频率、占空比以及死区计算详细讲解
    图像处理:双边滤波
    加满油箱问题
  • 原文地址:https://www.cnblogs.com/XB-like-kljc/p/XB-like-kljc001.html