作为学习stm32f103c8t6阶段的收官项目,这里做下总结,源码放在了最后。
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();
- }
- }
初始化一个是要开启串口中断,另外要把电机旋转90让超声波正对前方,再者就是显示空数据。
这里没有开启测速的中断TIM3,因为开启会影响DHT11的时序,导致其卡死到检测温湿度的while循环里。
- void init() {
- //开启串口中断蓝牙在用
- HAL_UART_Receive_IT(&huart1, &buf, 1);
- //开启pwm,并旋转至最前方
- sg90_init();
- //初始化oled
- oled_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 :--.--");
- }
停止模式时,温湿度正常在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);
- }
切换电机的模式,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);
- }
- }
定时器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++;
- }
循迹用的是pwm模式,原理是利用红外发射的光线有没有反射回来。
- void traceing() {
- if(runMode != lastMode) {
- lastMode = runMode;
- changeMode(PWM);
- HAL_Delay(500);
- // 处理oled
- oled_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();
- }
- }
主要是利用超声波检测左前右的障碍物的距离来决定如何前进。
- void avoid() {
- if(runMode != lastMode) {
- lastMode = runMode;
- changeMode(NORMAL);
- // 处理oled
- oled_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();
- }
- }
-
- }
-
- 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++;
- }
- //超声波echo
- if(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;
- }
-
- }
原理同循迹,so esay。
- void follow() {
- if(runMode != lastMode) {
- lastMode = runMode;
- changeMode(NORMAL);
-
- // 处理oled
- oled_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();
- }
- }
添加手势模块paj7620, 手势不可与DHT11共存,会影响手势的获取。
- void gesture() {
- if(runMode != lastMode) {
- lastMode = runMode;
- changeMode(NORMAL);
- // 处理oled
- oled_clear_1_line();
- oled_show_string(1,2,"mode : gesture");
- HAL_Delay(500);
- }
- paj7620_action();
- }
- void paj7620_action(void) {
- switch (status) {
- case 0x01: // up
- paj7620_up();
- break;
- case 0x02: // down
- paj7620_down();
- break;
- case 0x04: // left
- paj7620_left();
- break;
- case 0x08: // right
- paj7620_right();
- break;
- case 0x10: // push
- paj7620_push();
- break;
- case 0x20: // pop
- paj7620_pop();
- break;
- case 0x40: // rotate right
- paj7620_rotate_right();
- break;
- case 0x80: // rotate left
- paj7620_rotate_left();
- break;
- case 0x100:// wave
- paj7620_wave();
- break;
- case 0x00: // nothing
- paj7620_nothing();
- break;
- default:
- paj7620_error();
- }
- }
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、单片机和直流电机单独供电,防止因为电机电流的问题导致单片机复位。
10、paj7620刚开始上电之后需要复位才能正常运行,经调试在初始化的时,write时添加了300us的延时上电即可正常运行
跟随功能:
PB5 : 左红外传感器,输入模式
PA15: 右红外传感器,输入模式
循迹功能(原理和跟随一样):
PB3 : 左红外传感器,输入模式
PB4: 左红外传感器,输入模式
避障功能:
超声波
PA11 : sr04中的trig引脚,输出模式
PA12: sr04中的echo引脚,
输入模式(外部中断模式)TIM1: 用来超声波的计时
舵机
TIM4(PB9): 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没有进行控制
测速
PB10: 对应模块的out引脚,外部中断
手势(paj7620)
PA5: 对应paj7620的scl引脚,输出模式
PA6: 对应paj7620的sda引脚,输出模式
PA7: 对应paj7620的int引脚,外部下降沿中断
2023-10-2 13点:将超声波回应利用外部中断方式,这样在避障模式下,也可以进行测速
2023-10-4 :添加手势模块