作为学习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();
}
}
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点:将超声波回应利用外部中断方式,这样在避障模式下,也可以进行测速