电机在运行过程中,需要实时检测其转速的稳定性,有效反映电机的运行情况。
本文介绍了基于stm32的转速仪的设计,可以用光电门传感器和红外对管传感器测量,可以设置选择传感器、计数方式,可以显示测量结果。描述总体设计方案、器件选型,各模块原理分析、软件设计思路与过程。制作测试仪并从精度、稳定性等角度对其进行测试。测试得转速仪可以正常稳定工作,有结构简单、便携、易于操作等优点,适用于小型直流电机的转速测量。
本文展示用cubeMX+MDK制作一个红外测温枪的过程。
下载工程
关注公众号小电动车,回复"转速仪"获取工程文件及其他资料,建议结合keil工程阅读本文。
红外循迹模块与光耦测速模块分别实现远距测速与近程测速功能,测量小风扇时,每当有扇叶通过,模块输出一个脉冲,用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显示方向,同时用数码管辅助显示转速。
采用两节18650电池供电,经过降压稳压模块得到系统工作电压3.3V。
使用stm32f103c8t6最小系统板,处理速度数据、执行摇杆指令和控制显示模块。
使用红外循迹模块实现远程测速。
使用光电门模块实现近程测速。
使用双轴按键摇杆传感器模块,实现控制计数方式、切换传感器与显示屏翻转。
使用0.96寸OLED显示转速、计数方式和当前传感器。
使用四位数码管显示转速,由于测试设备转速有限,只启用两位,剩余两位备用。
使用面包板作为载体,结合杜邦线、铜柱等固定所有模块,完成一个手持仪器。
使用两节18650电池供电,经过电源模块得到8V、5V、3.3V。
使用三个开关分别接三个电压,另一边接直流减速电机,模拟三个档位。
使用直流减速电机驱动三扇叶小风扇,作为直接被测目标。
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-33芯片,将18650电池的8V电压转化为系统工作电压3V3
根据原理图3.2,使用Altium Designer 2019绘制PCB板
红外对射计数传感器TCRT5000可用于电度表脉冲数据检测、传真机碎纸机纸张检测、障碍检测、黑白线检测。
其特性为:检测反射距离在1mm~25mm之间,输出形式为数字开关量输出(0和1)。TCRT5000型传感器的红外线发射二极管不断发射红外线,当发射出的红外线没有被反射回来或被反射回来但强度不够大时,红外接收管一直处于熄灭状态;被检测物体出现在检测范围内时,红外线被反射回来其强度足够大,红外接收管饱和,此时模块的输出端为低电平,指示二极管被点亮。
对射光电传感器广泛应用于电机转速检测,脉冲记数,位置限位等。
其特点是模块槽中无遮挡时,接收管道通,模块DO输出低电平;遮挡时,DO输出高电平;DO输出接口可以与单片机IO口直接相连,检测传感器是否有遮挡,如用电机码盘则可以检测电机的转速;模块DO可以与继电器相连,组成限位开关等功能。
双轴按键传感器
两路模拟量输出,一路数字量输出
Y轴输出为两个电位器,可以通过AD转换读出扭动角度向下按摇杆,可以出动一路轻触开关,为数字输出,已上拉适用于两自由度舵机云台控制或者其他遥控比例控制。
使用双轴按键摇杆传感器模块,实现控制计数方式、切换传感器与显示屏翻转。
单片机选用stm32f103c8t6,开发平台为STM32CubeMX和Keil
MDK,使用HAL库开发。系统主频为72MHz。
开启stm32的定时器提供标准时间,计数器接收传感器脉冲,ADC接收摇杆XY控制信号,外部中断接收摇杆Z信号,硬件SPI与OLED通信,使用12个GPIO驱动数码管,USART留作调试接口。
红外循迹模块与光耦测速模块分别实现远距测速与近程测速功能,测量小风扇时,每当有扇叶通过,模块输出一个脉冲,用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显示方向,同时用数码管辅助显示转速。
开启stm32的定时器TIM3作为秒表,内部时钟输入。
stm32主频设置为72MHz。
设置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; //将此次计数器值保存为上一秒计数值
}
开启stm32的两个ADC采集摇杆模块的XY控制信号。由于当前ADC采集范围为0 3.3V,采集精度为12位。
故摇杆模块供电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是否按下。
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);
}
}
开启stm32的spi控制OLED屏幕,调用HAL库SPI函数和现成OLED库函数可以控制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的推挽输出模式,其中八个作为阳极显示数字,四个作为阴极选择显示位置。
显示单个数字函数如下:(显示位置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;
}
}
关注公众号小电动车,回复"转速仪"获取工程文件及其他资料,建议结合工程阅读本文。