stm32+HAL库制作转速仪

stm32+HAL库制作转速仪

前言

电机在运行过程中,需要实时检测其转速的稳定性,有效反映电机的运行情况。

本文介绍了基于stm32的转速仪的设计,可以用光电门传感器和红外对管传感器测量,可以设置选择传感器、计数方式,可以显示测量结果。描述总体设计方案、器件选型,各模块原理分析、软件设计思路与过程。制作测试仪并从精度、稳定性等角度对其进行测试。测试得转速仪可以正常稳定工作,有结构简单、便携、易于操作等优点,适用于小型直流电机的转速测量。

本文展示用cubeMX+MDK制作一个红外测温枪的过程。
下载工程
关注公众号小电动车,回复"转速仪"获取工程文件及其他资料,建议结合keil工程阅读本文。
在这里插入图片描述

效果

转速仪{#fig:转速仪}

总体方案设计

方案

红外循迹模块与光耦测速模块分别实现远距测速与近程测速功能,测量小风扇时,每当有扇叶通过,模块输出一个脉冲,用stm32的计数器接收脉冲并计数,同时开启定时器进行一秒定时,则KaTeX parse error: Undefined control sequence: \mbox at position 1: \̲m̲b̲o̲x̲{转速}=\frac{\mbo…

用stm32的adc与终端输入检测摇杆XYZ的输入,摇杆的Y向为切换扇叶数,范围为1 9,摇杆的X方向为切换红外测速(远距测速)和光电门测速(近程测速)。
stm32通过SPI与OLED通讯,显示屏显示转速、扇叶数和测速模式,按下摇杆的Z方向为切换OLED显示方向,同时用数码管辅助显示转速。

总体方案框图{#fig:总体方案框图}

供电模块

采用两节18650电池供电,经过降压稳压模块得到系统工作电压3.3V。

供电模块{#fig:供电模块}

主控

使用stm32f103c8t6最小系统板,处理速度数据、执行摇杆指令和控制显示模块。

stm32f103c8t6最小系统板{#fig:最小系统板}

扫描二维码关注公众号,回复: 14454841 查看本文章

测速模块

使用红外循迹模块实现远程测速。

红外循迹模块{#fig:红外循迹模块}

使用光电门模块实现近程测速。

槽型光耦传感器{#fig:槽型光耦传感器}

控制模块

使用双轴按键摇杆传感器模块,实现控制计数方式、切换传感器与显示屏翻转。

双轴按键摇杆传感器{#fig:双轴按键摇杆传感器}

显示模块

使用0.96寸OLED显示转速、计数方式和当前传感器。

0.96寸OLED显示屏模块{#fig:0.96寸OLED显示屏模块}

控制模块

使用四位数码管显示转速,由于测试设备转速有限,只启用两位,剩余两位备用。

数码管{#fig:数码管}

组装

使用面包板作为载体,结合杜邦线、铜柱等固定所有模块,完成一个手持仪器。
在这里插入图片描述

测试设备

使用两节18650电池供电,经过电源模块得到8V、5V、3.3V。
使用三个开关分别接三个电压,另一边接直流减速电机,模拟三个档位。
使用直流减速电机驱动三扇叶小风扇,作为直接被测目标。

手持测试仪{#fig:手持测试仪}

手持测试仪{#fig:手持测试仪}

手持测试仪{#fig:手持测试仪}

硬件设计

电源模块

AMS1117芯片

AMS1117是一个正向低压降稳压器,它的稳压调整管是由一个PNP驱动的NPN管组成的,在1A电流下压降为1.2V。有AMS1117固定输出版本和可调版本,固定输出电压1.5V,1.8V,2.5V,2.85V,3.0V,3.3V,5.0V,具有1%的精度,固定输出电压为1.2V的精度为2%,典型电路如图3.1。

AMS1117典型电路{#fig:AMS1117典型电路}

AMS1117典型电路

选用AMS1117-33芯片,将18650电池的8V电压转化为系统工作电压3V3

电源模块原理图{#fig:电源模块原理图}

PCB绘制

根据原理图3.2,使用Altium Designer 2019绘制PCB板

PCB板三维模型{#fig:PCB板三维模型}

测速模块

红外循迹模块

红外对射计数传感器TCRT5000可用于电度表脉冲数据检测、传真机碎纸机纸张检测、障碍检测、黑白线检测。

其特性为:检测反射距离在1mm~25mm之间,输出形式为数字开关量输出(0和1)。TCRT5000型传感器的红外线发射二极管不断发射红外线,当发射出的红外线没有被反射回来或被反射回来但强度不够大时,红外接收管一直处于熄灭状态;被检测物体出现在检测范围内时,红外线被反射回来其强度足够大,红外接收管饱和,此时模块的输出端为低电平,指示二极管被点亮。

红外循迹模块电路图{#fig:红外循迹模块电路图}

对射光电传感器

对射光电传感器广泛应用于电机转速检测,脉冲记数,位置限位等。

其特点是模块槽中无遮挡时,接收管道通,模块DO输出低电平;遮挡时,DO输出高电平;DO输出接口可以与单片机IO口直接相连,检测传感器是否有遮挡,如用电机码盘则可以检测电机的转速;模块DO可以与继电器相连,组成限位开关等功能。

光电门模块电路图{#fig:光电门模块电路图}

控制模块

双轴按键传感器

两路模拟量输出,一路数字量输出
Y轴输出为两个电位器,可以通过AD转换读出扭动角度向下按摇杆,可以出动一路轻触开关,为数字输出,已上拉适用于两自由度舵机云台控制或者其他遥控比例控制。

使用双轴按键摇杆传感器模块,实现控制计数方式、切换传感器与显示屏翻转。

软件设计

总体方案

单片机选用stm32f103c8t6,开发平台为STM32CubeMX和Keil
MDK,使用HAL库开发。系统主频为72MHz。

开启stm32的定时器提供标准时间,计数器接收传感器脉冲,ADC接收摇杆XY控制信号,外部中断接收摇杆Z信号,硬件SPI与OLED通信,使用12个GPIO驱动数码管,USART留作调试接口。

stm32资源使用情况{#fig:stm32资源使用情况}

红外循迹模块与光耦测速模块分别实现远距测速与近程测速功能,测量小风扇时,每当有扇叶通过,模块输出一个脉冲,用stm32的计数器接收脉冲并计数,同时开启定时器进行一秒定时,则。KaTeX parse error: Undefined control sequence: \mbox at position 1: \̲m̲b̲o̲x̲{转速}=\frac{\mbo…

用stm32的adc与终端输入检测摇杆XYZ的输入,摇杆的Y向为切换扇叶数,范围为1 9,摇杆的X方向为切换红外测速(远距测速)和光电门测速(近程测速)。

stm32通过SPI与OLED通讯,显示屏显示转速、扇叶数和测速模式,按下摇杆的Z方向为切换OLED显示方向,同时用数码管辅助显示转速。

程序流程图{#fig:程序流程图}

定时器/计数器

开启stm32的定时器TIM3作为秒表,内部时钟输入。

stm32主频设置为72MHz。

stm32时钟树{#fig:stm32时钟树}

设置TIM3分频数为7199,计数周期为9999,则计数频率
f = 72 , 000 , 000 H z ( 7199 + 1 ) ( 9999 + 1 ) = 1 H z f=\frac{72,000,000Hz}{(7199+1)(9999+1)}=1Hz f=(7199+1)(9999+1)72,000,000Hz=1Hz

即每秒定时一次,同时开启中断,则每秒钟单片机会进入一次中断。

在这里插入图片描述
在这里插入图片描述

开启stm32计数器TIM1、TIM2接收测速传感器的脉冲,输入方式为外部脉冲,同时开启最大滤波以排除干扰。

在这里插入图片描述
在这里插入图片描述

则每当进入秒表中断时,读取计数器寄存器的值,并将该值与上一秒计数器的值作差(第一次时上一秒为0),得到的差即为计数值,该值除以叶片数(由摇杆Y方向控制,初始值为3)得到秒转速,该值可以提供给OLED显示。最后将当前计数器值保存为上一秒计数器值,以便下次调用。

定时器的中断回调函数放在文件counter.c中,程序如下

uint16_t CountThis, CountLast, CountDis;    //定义计数器的值、上一秒计数器的值、转速
extern uint8_t Leaves, ModuleFlag;      //定义叶片数、传感器标志
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器中断回调函数
{
    if(ModuleFlag == 0){                //采集红外传感器脉冲
        CountThis = htim2.Instance->CNT;}
    else{                               //采集光电门传感器脉冲
        CountThis = htim1.Instance->CNT;}
    CountDis = (CountThis - CountLast)/Leaves;//计算转速
    //    printf("\t%d r/s\r\n",CountDis);
    OLED_ShowNum(48,0,CountDis,3,16);//OLED显示刷新
    OLED_Refresh();
    CountLast = CountThis;          //将此次计数器值保存为上一秒计数值
}

ADC

开启stm32的两个ADC采集摇杆模块的XY控制信号。由于当前ADC采集范围为0 3.3V,采集精度为12位。

stm32ADC参数配置{#fig:stm32ADC参数配置}

故摇杆模块供电VCC=3.3V,则当当X或Y轴不偏时,VRX/VRY管脚电压为1.65V;X/Y轴正方向偏到最大时,VRX/VRY管脚电压为3.3V;X/Y轴负方向偏到最大时,VRX/VRY管脚电压为0V。此时用ADC管脚接收,读取ADC寄存器可分别得数值2000、4096、0即代表不偏、正偏最大、反偏。

由三个值可判断摇杆的控制信号,设定当Y轴正偏一次,叶片数+1,反偏一次,叶片数-1;当X轴正偏或反偏则切换两个传感器。

ADC的采集放在main函数的while中,以5ms/次的频率扫描,程序如下:

while (1)
{
    Dis_2Num(CountDis);             //数码管显示
    HAL_Delay(5);                       //每5ms进行一次采集
    HAL_ADC_Start(&hadc1);              //开启adc1
    VRX = HAL_ADC_GetValue(&hadc1);     //采集adc1的值
    HAL_ADC_Start(&hadc2);              //开启adc2
    VRY = HAL_ADC_GetValue(&hadc2); //采集adc2的值 
    if(VRY > 3500){                     //当VRY>3500,认为Y轴正偏一次
        adc_Flag ++;
        HAL_Delay(10);
        if(adc_Flag == 2){
            if(Leaves<9)Leaves ++;      //叶片数+1
            OLED_ShowNum(56,16,Leaves,1,16);//叶片数显示刷新
            OLED_Refresh();
            adc_Flag = 0;
            HAL_Delay(400);             //增加延时避免一次识别为多次
        }
    }
    else if(VRY < 500){                 //当VRY<500,认为Y轴正偏一次
        adc_Flag ++;
        HAL_Delay(10);
        if(adc_Flag == 2){
            if(Leaves>1)Leaves --;      //叶片数-1
            OLED_ShowNum(56,16,Leaves,1,16); //叶片数显示刷新
            OLED_Refresh();
            adc_Flag = 0;
            HAL_Delay(400);             //增加延时避免一次识别为多次
        }
    } 
    if((VRX > 3500)||(VRX < 500)){          //当VRX<500||VRX>3500,认为X轴偏一次
        mol_Flag ++;
        HAL_Delay(10);
        if(mol_Flag == 2){
            if(ModuleFlag == 0){            //当前传感器为红外传感器时
                ModuleFlag = 1;     //切换为光电门传感器
                OLED_ShowChinese(32, 48, 8, 16);//显示刷新
                OLED_ShowChinese(48, 48, 9, 16);
            }
            else{                       //当前传感器为光电门传感器时
                ModuleFlag = 0;     //切换为红外传感器
                OLED_ShowChinese(32, 48, 4, 16);//显示刷新
                OLED_ShowChinese(48, 48, 5, 16);
            }
            mol_Flag = 0;
            HAL_Delay(400);             //增加延时避免一次识别为多次
        }
    }
}

外部中断

在遥感模块VCC=3.3V,SW管脚接上拉电阻前提下,当Z不按下时SW管脚为3.3V,当Z按下时SW管脚为0V。

故开启管脚PA3的外部中断输入模式,接上拉电阻,检测上升沿,即可判断Z是否按下。

TIM3配置{#fig:TIM3配置}

extern uint8_t TurnFlag;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//中断回调函数
{
    if(GPIO_Pin == KEY_Z_Pin)                 //判断PA3接收到中断
    {
        if(TurnFlag == 1)TurnFlag = 0;
        else TurnFlag = 1;
        OLED_DisplayTurn(TurnFlag);       //反转屏幕指令
        HAL_Delay(100);
    }
}

SPI

开启stm32的spi控制OLED屏幕,调用HAL库SPI函数和现成OLED库函数可以控制OLED显示中文、数字、字幕。

0.96寸OLED显示内容{#fig:0.96寸OLED显示内容}

直接调用的上层函数有:

OLED_Init();                        //OLED初始化
OLED_Clear();                       //清除显示内容
OLED_DisplayTurn(TurnFlag);     //显示方向设置
OLED_ShowChinese(0, 0, 0, 16);      //显示中文
OLED_Printf(80, 0, "r/s", 16);          //显示字母
OLED_ShowNum(56,16,Leaves,1,16);    //显示数字
OLED_Refresh();                 //显示刷新

数码管

由数码管的原理,由于本次显示内容少,显示要求低,stm32主频较高,故采用循环显示的方式。

由于stm32GPIO口的推挽输出模式可以提供足够大的电流,使LED有较高亮度,故本次不需要放大,直接在LED的阳极接推挽输出高电平,在LED的阴极接推挽输出低电平,配合控制代码可以显示数字。

开启stm32的十二个GPIO的推挽输出模式,其中八个作为阳极显示数字,四个作为阴极选择显示位置。

数码管相关GPIO配置情况{#fig:数码管相关GPIO配置情况}

显示单个数字函数如下:(显示位置1,数字1,其余同理省略)

void Dis_Num(uint8_t DIG, uint8_t NUM)
{
    switch(DIG)
    {
        case 1:
        HAL_GPIO_WritePin(DIG1_GPIO_Port,DIG1_Pin,GPIO_PIN_RESET);
        HAL_GPIO_WritePin(DIG2_GPIO_Port,DIG2_Pin,GPIO_PIN_SET);
        HAL_GPIO_WritePin(DIG3_GPIO_Port,DIG3_Pin,GPIO_PIN_SET);
        HAL_GPIO_WritePin(DIG4_GPIO_Port,DIG4_Pin,GPIO_PIN_SET);
        break;
        ……
    }
    switch(NUM)
    {
        case 1:
        HAL_GPIO_WritePin(A_GPIO_Port,A_Pin,GPIO_PIN_RESET);
        HAL_GPIO_WritePin(B_GPIO_Port,B_Pin,GPIO_PIN_SET);
        HAL_GPIO_WritePin(C_GPIO_Port,C_Pin,GPIO_PIN_SET);
        HAL_GPIO_WritePin(D_GPIO_Port,D_Pin,GPIO_PIN_RESET);
        HAL_GPIO_WritePin(E_GPIO_Port,E_Pin,GPIO_PIN_RESET);
        HAL_GPIO_WritePin(F_GPIO_Port,F_Pin,GPIO_PIN_RESET);
        HAL_GPIO_WritePin(G_GPIO_Port,G_Pin,GPIO_PIN_RESET);
        HAL_GPIO_WritePin(DP_GPIO_Port,DP_Pin,GPIO_PIN_RESET);
        break;
        ……
    }
}

显示两个数字可通过除和取余提取个位与十位实现,函数如下:

uint8_t NumFlag = 0;            //个位与十位显示顺序标志,交换顺序使两位显示时长对称
void Dis_2Num(uint8_t Number)
{
    uint8_t Decade,Uint;
    Decade = Number/10;     //除法取十位
    Uint = Number%10;       //取余取个位
    if(NumFlag == 0){
        Dis_Num(2,Decade);  //显示十位
        Dis_Num(3,Uint);        //显示个位
        NumFlag = 1;
    }
    else{
        Dis_Num(3,Uint);
        Dis_Num(2,Decade);
        NumFlag = 0;
    }
}

显示效果{#fig:显示效果}

到此转速仪已经完成。
在这里插入图片描述

下载

关注公众号小电动车,回复"转速仪"获取工程文件及其他资料,建议结合工程阅读本文。
在这里插入图片描述


猜你喜欢

转载自blog.csdn.net/weixin_46143152/article/details/113917650