stm32之智能小车总结

        作为学习stm32f103c8t6阶段的收官项目,这里做下总结,源码放在了最后。

一、功能描述

  • 1、跟随功能
  • 2、循迹功能
  • 3、避障功能
  • 4、测速功能
  • 5、温湿度常显
  • 6、oled显示
  • 7、语音或蓝牙进行功能切换
  • 8、手势功能

二、主要代码解析

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();
	//初始化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 :--.--");
}

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);
		// 处理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();
	}
}

2.7、避障

主要是利用超声波检测左前右的障碍物的距离来决定如何前进。

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();
			}
		}

}

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++;
	}
	//超声波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;
	}
	
}

2.8、跟随

原理同循迹,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();
	}
}

2.9、手势

添加手势模块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 :添加手势模块

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

俯仰一世_1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值