作为学习stm32f103c8t6阶段的收官项目,这里做下总结,源码放在了最后。
一、功能描述
- 1、跟随功能
- 2、循迹功能
- 3、避障功能
- 4、测速功能
- 5、温湿度常显
- 6、oled显示
- 7、语音或蓝牙进行功能切换
二、主要代码解析
2.1、main
main函数主要展示while里的功能,具体的实现在car_function文件内。
int main(void)
{HAL_Init();MX_GPIO_Init();MX_USART1_UART_Init();MX_TIM2_Init();MX_TIM4_Init();MX_TIM1_Init();MX_TIM3_Init();MX_I2C1_Init();init();while (1){get_mode();reset();switch(runMode) {case tracingMode: traceing();break;case followMode:follow();break;case avoidMode:avoid();break;case stopMode:stop_car();break;}display_temp_humi();}
}
2.2、init
初始化一个是要开启串口中断,另外要把电机旋转90让超声波正对前方,再者就是显示空数据。
这里没有开启测速的中断TIM3,因为开启会影响DHT11的时序,导致其卡死到检测温湿度的while循环里。
void init() {//开启串口中断蓝牙在用HAL_UART_Receive_IT(&huart1, &buf, 1);//开启pwm,并旋转至最前方sg90_init();//初始化oledoled_init();oled_clear_all();oled_show_string(1,2,"mode : ready");oled_show_string(2,2, "speed: 0cm/s");oled_show_string(3,2, "Temp :--.--");oled_show_string(4,2, "Temp :--.--");
}
2.3、display_temp_humi
停止模式时,温湿度正常在main 函数里正常检测,在小车的其它模式,要限制温湿度检测的频率,否则会影响小车的运行。小车的其它模式要比温湿度优先级高。在非停止模式时,这里会计数,当计数到50w次时才会进行一次检测。
void display_temp_humi() {// 停止模式时正常检测湿度,非停止模式,计数50w检测一次if(runMode != stopMode) {count ++;if(count <= 500000) { return; }count = 0;} count = 0;//记得关中断,否则会影响DHT11采集数据HAL_TIM_Base_Stop_IT(&htim3);char msg[16];uint8_t result = trig_dht();receive_data();oled_clear(4, 8, 56, 128);sprintf(msg, "Temp : %d.%d C", datas[2], datas[3]);oled_show_string(3,2,msg);sprintf(msg, "Humi : %d.%d %%", datas[0], datas[1]);oled_show_string(4,2,msg);;HAL_Delay(500);
}
2.4、changeMode
切换电机的模式,PWM 是变速模式,主要用在寻迹模式,NORMAL是正常模式,用在跟随和避障模式。L0110s 部分引脚要接入到stm32具体pwm功能的引脚上。模式的切换要重新初始化相应的引脚。
void changeMode(uint8_t m) {mode = m;if(mode == NORMAL) {HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_1);HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_2);init_port();} else {MX_TIM2_Init();HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);HAL_Delay(500);}
}
2.5、测速
定时器1s,查看外部中断进入了多少次。即可算出速度(当前测速模块有问题,速度偏大)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {// 展示速度//oled部分清屏sprintf(speedString, "speed:%4dcm/s", speedCnt);//old__clear_bottom_half();oled_show_string(2,2,speedString);speedCnt = 0;
}void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {if(GPIO_Pin != GPIO_PIN_10) {return ;}if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10) == GPIO_PIN_RESET)speedCnt++;
}
2.6、循迹
循迹用的是pwm模式,原理是利用红外发射的光线有没有反射回来。
void traceing() {if(runMode != lastMode) {lastMode = runMode;changeMode(PWM);HAL_Delay(500);// 处理oledoled_clear_1_line();oled_show_string(1,2,"mode : trace");}if(leftTraceValue() == GPIO_PIN_RESET && rightTraceValue() == GPIO_PIN_RESET) {forward();}if(leftTraceValue() == GPIO_PIN_RESET && rightTraceValue() == GPIO_PIN_SET) {leftward();}if(leftTraceValue() == GPIO_PIN_SET && rightTraceValue() == GPIO_PIN_RESET) {rightward();}if(leftTraceValue() == GPIO_PIN_SET && rightTraceValue() == GPIO_PIN_SET) {stop();}
}
2.7、避障
主要是利用超声波检测左前右的障碍物的距离来决定如何前进。
void avoid() {if(runMode != lastMode) {lastMode = runMode;changeMode(NORMAL);// 处理oledoled_clear_1_line();oled_show_string(1,2,"mode : avoid");HAL_Delay(500);}if(dir != MIDDLE) {dir = MIDDLE;turn_90_degree();HAL_Delay(300);}disMiddle = get_distance();if(disMiddle > 35) {forward();} else if(disMiddle < 10) {backward();} else {stop();turn_180_degree();HAL_Delay(300);disLeft = get_distance();turn_90_degree();HAL_Delay(300);turn_0_degree();dir = RIGHT;HAL_Delay(300);disRight = get_distance();if(disLeft < disRight) {rightward();HAL_Delay(150);stop();} if(disLeft > disRight){leftward();HAL_Delay(150);stop();}}}
2023-10-2 13点 超声波中断
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {//脉冲测速if(GPIO_Pin == GPIO_PIN_10) {if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10) == GPIO_PIN_RESET)speedCnt++;}//超声波echoif(GPIO_Pin == GPIO_PIN_12) {//while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == GPIO_PIN_RESET);HAL_TIM_Base_Start(&htim1);__HAL_TIM_SetCounter(&htim1, 0);while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == GPIO_PIN_SET);HAL_TIM_Base_Stop(&htim1);int cnt = __HAL_TIM_GetCounter(&htim1);distance = 340 * 0.000001 * cnt * 100 / 2;}}
2.8、跟随
原理同循迹,so esay。
void follow() {if(runMode != lastMode) {lastMode = runMode;changeMode(NORMAL);// 处理oledoled_clear_1_line();oled_show_string(1,2,"mode : follow");HAL_Delay(100);}if(leftFollowValue() == GPIO_PIN_RESET && rightFollowValue() == GPIO_PIN_RESET) {forward();}if(leftFollowValue() == GPIO_PIN_RESET && rightFollowValue() == GPIO_PIN_SET) {leftward();}if(leftFollowValue() == GPIO_PIN_SET && rightFollowValue() == GPIO_PIN_RESET) {rightward();}if(leftFollowValue() == GPIO_PIN_SET && rightFollowValue() == GPIO_PIN_SET) {stop();}
}
三、效果
stm32小车
四、问题记录
1、计数定时器(TIM3)中断影响DHT11单总线的时序,导致会卡死到检测温度的while里,导致执行不了main函数里while的其它功能(小车模式切换),但是定时器中断还是可以运行的。
2、同理也会卡超声波while,所以开启避障模式时也要把TIM3关了。(利用外部中断,不用再关TIM)
3、PWM模式和GPIO的功能切换,起初以为只能在初始化里使用一种模式,但在调试之后发现是可以进行转换的。
4、oled是可以进行局部清屏的,oled清屏函数记录-CSDN博客。
5、修改蓝牙波特率,用错了指令,导致一直未成功。HC08 AT指令
6、触发DHT11和获取数据之间不能插入printf函数,否则会卡死到 while 循环里,效果同1
7、小车变速PWM模式时,在使用CubeMx配置引脚时,要把所有引脚输出低电平,否则PWM会不起作用。
8、面板包接线容易松动,导致语音模式引脚输出的电平乱跳。
9、单片机和直流电机单独供电,防止因为电机电流的问题导致单片机复位。
五、配置
跟随功能:
PB5 : 左红外传感器,输入模式
PA15: 右红外传感器,输入模式
循迹功能(原理和跟随一样):
PB3 : 左红外传感器,输入模式
PB4: 左红外传感器,输入模式
避障功能:
超声波
PA11 : sr04中的trig引脚,输出模式
PA12: sr04中的echo引脚,
输入模式(外部中断模式)TIM1: 用来超声波的计时
舵机
PA11 : sr04中的trig引脚,输出模式
PA15: sr04中的echo引脚,输入模式
TIM4: channel 4用来控制舵机的旋转角度
温湿度功能(DHT11):
PA8 : dht11中的Data引脚,(初始化时先配置成输入模式!!!,在需要的时候再切换成输出模式)
Oled
PB6(SCL) : 使用stm32 IIC1接口中的SCL
PB7(SDA): 使用stm32 IIC1接口中的SDA
蓝牙(hc04)
PA9(USART1_TX) : 使用stm32 串口1中的输出
PA10(USART1_RX): 使用stm32 串口1中的输入
语音(su03)
PB11: 对应su03的a25引脚,输入模式
PB12: 对应su03的a26引脚,输入模式
PB13: 对应su03的a27引脚,输入模式
直流电机 和 L9110S模块
PA0: 对应L9110s的B-1B引脚,TIM2中channle 1 的PWM模式
PA1: 对应L9110s的A-1B引脚,TIM2中channle 2 的PWM模式
PB0: 对应L9110s的B-1A引脚
PB2: 对应L9110s的A-1A引脚
l9110s中接线会影响电机的前进和后退,根据实际情况来处理引脚的配置,这里PA0 和 PA1 是控制前面的速度,PB0和PB2没有进行控制
完整代码Github ----smart_car
Release 0.0.1 · barry-source/embedded (github.com)
六、修订
2023-10-2 13点:将超声波回应利用外部中断方式,这样在避障模式下,也可以进行测速