STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-2
https://developer.aliyun.com/article/1507941
4.测速小车
4.1 测速模块
- 用途:广泛用于电机转速检测,脉冲计数,位置限位等。
- 有遮挡,输出高电平;无遮挡,输出低电平
- 接线 :VCC 接电源正极3.3-5V
- GND 接电源负极 DO TTL开关信号输出
- AO 此模块不起作用
4.2 测试原理和单位换算
- 轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
- 对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平), 那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
- 定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
- 假设一秒有80脉冲,那么就是80cm/s
- 4.3 定时器和中断实现测速开发和调试代码
测试数据通过串口发送到上位机
硬件接线
测速模块:
- VCC -- 3.3V 不能接5V,否则遮挡一次会触发3次中断
- OUT -- PB14
cubeMX配置
代码实现:
unsigned int speedCnt; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_14) if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET) speedCnt++; } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { printf("speed: %d\r\n", speedCnt); speedCnt = 0; } main函数里: HAL_TIM_Base_Start_IT(&htim2);
4.4 小车速度显示在OLED屏
OLED模块介绍:STM32 OLED屏幕显示详解
硬件接线
- SCL -- PB6
- SDA -- PB7
代码示例:
oled.c
#include "oled.h" #include "i2c.h" #include "oledfont.h" void Oled_Write_Cmd(uint8_t dataCmd) { HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT, &dataCmd, 1, 0xff); } void Oled_Write_Data(uint8_t dataData) { HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT, &dataData, 1, 0xff); } void Oled_Init(void){ Oled_Write_Cmd(0xAE);//--display off Oled_Write_Cmd(0x00);//---set low column address Oled_Write_Cmd(0x10);//---set high column address Oled_Write_Cmd(0x40);//--set start line address Oled_Write_Cmd(0xB0);//--set page address Oled_Write_Cmd(0x81); // contract control Oled_Write_Cmd(0xFF);//--128 Oled_Write_Cmd(0xA1);//set segment remap Oled_Write_Cmd(0xA6);//--normal / reverse Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64) Oled_Write_Cmd(0x3F);//--1/32 duty Oled_Write_Cmd(0xC8);//Com scan direction Oled_Write_Cmd(0xD3);//-set display offset Oled_Write_Cmd(0x00);// Oled_Write_Cmd(0xD5);//set osc division Oled_Write_Cmd(0x80);// Oled_Write_Cmd(0xD8);//set area color mode off Oled_Write_Cmd(0x05);// Oled_Write_Cmd(0xD9);//Set Pre-Charge Period Oled_Write_Cmd(0xF1);// Oled_Write_Cmd(0xDA);//set com pin configuartion Oled_Write_Cmd(0x12);// Oled_Write_Cmd(0xDB);//set Vcomh Oled_Write_Cmd(0x30);// Oled_Write_Cmd(0x8D);//set charge pump enable Oled_Write_Cmd(0x14);// Oled_Write_Cmd(0xAF);//--turn on oled panel } void Oled_Screen_Clear(void){ char i,n; Oled_Write_Cmd (0x20); //set memory addressing mode Oled_Write_Cmd (0x02); //page addressing mode for(i=0;i<8;i++){ Oled_Write_Cmd(0xb0+i); Oled_Write_Cmd(0x00); Oled_Write_Cmd(0x10); for(n=0;n<128;n++)Oled_Write_Data(0x00); } } void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2 unsigned int i; Oled_Write_Cmd(0xb0+(row*2-2)); //page 0 Oled_Write_Cmd(0x00+(col&0x0f)); //low Oled_Write_Cmd(0x10+(col>>4)); //high for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){ Oled_Write_Data(F8X16[i]); //写数据oledTable1 } Oled_Write_Cmd(0xb0+(row*2-1)); //page 1 Oled_Write_Cmd(0x00+(col&0x0f)); //low Oled_Write_Cmd(0x10+(col>>4)); //high for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){ Oled_Write_Data(F8X16[i]); //写数据oledTable1 } } /******************************************************************************/ // 函数名称:Oled_Show_Char // 输入参数:oledChar // 输出参数:无 // 函数功能:OLED显示单个字符 /******************************************************************************/ void Oled_Show_Str(char row,char col,char *str){ while(*str!=0){ Oled_Show_Char(row,col,*str); str++; col += 8; } }
main.c
extern uint8_t buf; unsigned int speedCnt = 0; char speedMes[24]; //主程序发送速度数据的字符串缓冲区 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_14) if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET) speedCnt++; } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { printf("speed: %d\r\n", speedCnt); sprintf(speedMes,"speed:%2d cm/s",speedCnt);//串口数据的字符串拼装,speed是格子,每个格子1cm Oled_Show_Str(2,2,speedMes); speedCnt = 0; }
5.远程控制小车
5.1 蓝牙控制小车
- 使用蓝牙模块,串口透传
- 蓝牙模块,又叫做蓝牙串口模块
串口透传技术:
- 透传即透明传送,是指在数据的传输过程中,通过无线的方式这组数据不发生任何形式的改变,仿 佛传输过程是透明的一样,同时保证传输的质量,原封不动地到了最终接收者手里。
- 以太网,蓝牙,Zigbee, GPRS 等模块玩法一样,对嵌入式程序员来说,不需要关心通讯模块内部数据 及协议栈工作原理,只要通过串口编程获得数据即可
代码实现:
整合前面串口控制小车代码,接入蓝牙模块
if (!strcmp(UART1_RX_Buffer, "M1")) { goForward(); HAL_Delay(10); } else if (!strcmp(UART1_RX_Buffer, "M2")) { goBack(); HAL_Delay(10); } else if (!strcmp(UART1_RX_Buffer, "M3")) { goLeft(); HAL_Delay(10); } else if (!strcmp(UART1_RX_Buffer, "M4")) { goRight(); HAL_Delay(10); } else stop();
5.2 蓝牙控制并测速小车
原理:运用上面讲到的蓝牙模块和测速模块,将两者代码整合
5.3 wifi控制测速小车
- Wifi模块-ESP-01s
- 蓝牙,ESP-01s,Zigbee, NB-Iot等通信模块都是基于AT指令的设计
AT指令介绍:
- AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。
- 其对所传输的数据包大小有定义:即对于AT指令的发送,除AT两个字符外,最多可以接收1056个 字符的长度(包括最后的空字符)。
- 每个AT命令行中只能包含一条AT指令;对于由终端设备主动向PC端报告的URC指示或者response 响应,也要求一行最多有一个,不允许上报的一行中有多条指示或者响应。AT指令以回车作为结 尾,响应或上报以回车换行为结尾。
硬件接线
- 把esp8266插进串口1
使用方法:
Wifi模块-ESP-01s_esp01s波特率-CSDN博客
5.4 4g控制小车
原理:运用EC03-DNC4G通信模块
模块介绍:
- 基于串口AT指令的开发方式
- 有两种工作模式,默认是透传模式,通过其他方式进入AT指令模式
- 注意插卡不要出错,下图红色位置为SIM卡状态灯,亮才是正常
代码不做修改,直接基于蓝牙小车整合, 4g模块只要做好外网透传就可以了
6.语音控制小车
6.1语音模块配置:
使用SU-03T / LD3320
具体介绍看我之前写过的博客:SU-03T语音模块的使用_罗小白的干爹的博客-CSDN博客
6.2 语音控制小车开发和调试代码
硬件接线:
循迹小车:
- 循迹模块(左) -- PB3
- 循迹模块(右) -- PB4
跟随小车:
- 跟随模块(左) -- PA8
- 跟随模块(右) -- PA9
避障小车:
- sg90:PB9
- Trig:PA10
- Echo:PA11
OLED模块:
- SCL -- PB6
- SDA -- PB7
语音模块:
- A25 -- PA15 (跟随)
- A26 -- PA13 (避障)
- A27 -- PA14 (循迹)
cubeMX配置
代码示例:
#include "main.h" #include "i2c.h" #include "tim.h" #include "gpio.h" #include "sg90.h" #include "sr04.h" #include "motor.h" #include "oled.h" #define MIDDLE 0 #define LEFT 1 #define RIGHT 2 #define BZ 1 #define XJ 2 #define GS 3 #define LeftWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) #define RightWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) #define LeftWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) #define RightWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9) #define A25 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15) #define A26 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_13) #define A27 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_14) void SystemClock_Config(void); char dir; void xunjiMode() { if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_RESET) goForward(); if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_RESET) goLeft(); if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_SET) goRight(); if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_SET) stop(); } void gensuiMode() { if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_RESET) goForward(); if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_RESET) goRight(); if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_SET) goLeft(); if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_SET) stop(); } void bizhangMode() { double disMiddle; double disLeft; double disRight; if(dir != MIDDLE){ sgMiddle(); dir = MIDDLE; HAL_Delay(300); } disMiddle = get_distance(); if(disMiddle > 35){ //前进 goForward(); }else if(disMiddle < 10){ goBack(); }else { //停止 stop(); //测左边距离 sgLeft(); HAL_Delay(300); disLeft = get_distance(); sgMiddle(); HAL_Delay(300); sgRight(); dir = RIGHT; HAL_Delay(300); disRight = get_distance(); if(disLeft < disRight){ goRight(); HAL_Delay(150); stop(); } if(disRight < disLeft){ goLeft(); HAL_Delay(150); stop(); } } HAL_Delay(50); } int main(void) { int mark = 0; HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM4_Init(); MX_TIM2_Init(); MX_I2C1_Init(); initSG90(); HAL_Delay(1000); dir = MIDDLE; Oled_Init(); Oled_Screen_Clear(); Oled_Show_Str(2,2,"-----Ready----"); while (1) { //满足寻迹模式的条件 if(A25 == 1 && A26 == 1 && A27 == 0){ if(mark != XJ){ Oled_Screen_Clear(); Oled_Show_Str(2,2,"-----XunJi----"); } mark = XJ; xunjiMode(); } //满足跟随模式的条件 if(A25 == 0 && A26 == 1 && A27 == 1){ if(mark != GS){ Oled_Screen_Clear(); Oled_Show_Str(2,2,"-----GenSui----"); } mark = GS; gensuiMode(); } //满足避障模式的条件 if(A25 == 1 && A26 == 0 && A27 == 1){ if(mark != BZ){ Oled_Screen_Clear(); Oled_Show_Str(2,2,"-----BiZhang----"); } mark = BZ; bizhangMode(); } } }