萬(wàn)網(wǎng)個(gè)人網(wǎng)站備案查詢(xún)東莞今天的最新通知
項(xiàng)目框圖
ST-link接線
實(shí)物圖:
正面:
?反面:
相關(guān)內(nèi)容
使用L9110S電機(jī)模塊
電機(jī)驅(qū)動(dòng)模塊L9110S詳解 | 良許嵌入式
測(cè)速模塊
?
語(yǔ)音模塊SU-03T
網(wǎng)站:智能公元/AI產(chǎn)品零代碼平臺(tái)
一、讓小車(chē)動(dòng)起來(lái)
新建文件夾智能小車(chē)項(xiàng)目
在里面復(fù)制19-串口打印功能
重命名為01-讓小車(chē)動(dòng)起來(lái)
新建文件夾motor,如下圖所示
打開(kāi)項(xiàng)目
加載文件
先更改引腳
然后分裝函數(shù)
前進(jìn),后退,左轉(zhuǎn),右轉(zhuǎn),停止函數(shù)
PB0、PB1引腳控制的是左輪:
當(dāng)PB0引腳為高電平,PB1引腳為低電平的時(shí)候,左輪向前走
當(dāng)PB0引腳為低電平,PB1引腳為高電平的時(shí)候,左輪向后走
當(dāng)PB0引腳與PB1引腳電平相同的時(shí)候,左輪停止
PB2、PB10引腳控制的是右輪
當(dāng)PB2引腳為高電平,PB10引腳為低電平的時(shí)候,右輪向前走
當(dāng)PB2引腳為低電平,PB10引腳為高電平的時(shí)候,右輪向后走
當(dāng)PB2引腳與PB10引腳電平相同的時(shí)候,右輪停止
#include "motor.h"//初始化電機(jī)函數(shù)
void motor_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打開(kāi)時(shí)鐘__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB時(shí)鐘//調(diào)用GPIO初始化函數(shù)gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10; // 兩個(gè)LED對(duì)應(yīng)的引腳gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽輸出gpio_initstruct.Pull = GPIO_PULLUP; // 上拉gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(GPIOB, &gpio_initstruct);motor_stop();
}
//前進(jìn)
void motor_go_forward(void)
{//左輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);//右輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
}
//后退
void motor_go_backward(void)
{//左輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);//右輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
}
//左轉(zhuǎn)
void motor_go_left(void)
{//左輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);//想要一個(gè)輪子不動(dòng),就讓它兩個(gè)引腳都等于reset或者setHAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);//右輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
}
//右轉(zhuǎn)
void motor_go_right(void)
{//左輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);//右輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
}
//停止
void motor_stop(void)
{//左輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); //右輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
}
二、串口控制小車(chē)
注意:串口控制小車(chē)與藍(lán)牙控制小車(chē)類(lèi)似
想要實(shí)現(xiàn)的效果是:
M1:前進(jìn)
M2:后退
M3:左轉(zhuǎn)
M4:右轉(zhuǎn)
其他M:停止
復(fù)制01-讓小車(chē)動(dòng)起來(lái)
重命名為02-串口控制小車(chē)
將藍(lán)牙模塊的文件粘貼到改項(xiàng)目中進(jìn)行修改
打開(kāi)項(xiàng)目文件
加載文件
將藍(lán)牙模塊的串口2代碼粘貼到02項(xiàng)目中
將接收到的串口2的數(shù)據(jù)打印到串口1中
編譯
調(diào)試代碼
燒錄
記得把串口2連接上,用兩個(gè)串口助手顯示結(jié)果
串口助手1->連接串口1->用于接收串口2發(fā)送過(guò)來(lái)的數(shù)據(jù)->波特率是115200
串口助手2->連接串口2->用于發(fā)送數(shù)據(jù)(M1\M2\M3\M4\其他數(shù)據(jù))->波特率是9600
注意:在燒錄完代碼之后,一切準(zhǔn)備就緒,發(fā)現(xiàn)在串口2中發(fā)送輸入,串口1接收不到,這時(shí)候不要慌,先把兩個(gè)串口助手?jǐn)嚅_(kāi),然后再次選擇串口號(hào),連接串口,這時(shí)候在輸入數(shù)據(jù),就會(huì)有反應(yīng)了。
注意:如果你的電腦中usb接口不足,板子選擇使用面包板接電就會(huì)出現(xiàn)一個(gè)小錯(cuò)誤,面包板中只有一個(gè)3V3在供電,這個(gè)時(shí)候,使用杜邦線給32板子接電,就會(huì)出現(xiàn)在串口2中輸入M1前進(jìn)之后再輸入M2后退,小車(chē)會(huì)停下來(lái),串口助手1中會(huì)顯示hello word。如下圖所示:
再次輸入M2小車(chē)會(huì)后退,這是因?yàn)楣╇姴蛔?#xff0c;電壓不穩(wěn),所以想要達(dá)到輸入M1就是前進(jìn),輸入M2就是后退,解決辦法:需要使用USB外接電源供電。
三、藍(lán)牙控制小車(chē)
將上述的串口控制小車(chē)中的串口2所接的引腳替換成藍(lán)牙模塊
然后手機(jī)連接藍(lán)牙
輸入數(shù)據(jù),小車(chē)就會(huì)行動(dòng)
要注意藍(lán)牙所設(shè)置的波特率,不然不能正確接收數(shù)據(jù)
藍(lán)牙當(dāng)初所設(shè)置的波特率是115200,所以要在主函數(shù)中調(diào)用藍(lán)牙初始化函數(shù)時(shí)更改波特率
?
四、點(diǎn)動(dòng)控制小車(chē)
????????當(dāng)摁下前進(jìn)按鍵的時(shí)候,小車(chē)就會(huì)瘋狂的向前走,那么現(xiàn)在要實(shí)現(xiàn),當(dāng)摁一下前進(jìn),小車(chē)就前進(jìn)一下,實(shí)現(xiàn)點(diǎn)動(dòng)控制小車(chē)。
復(fù)制項(xiàng)目文件02,重命名為,點(diǎn)動(dòng)控制小車(chē)
打開(kāi)項(xiàng)目
只添加這兩行代碼即可
剩下的在軟件上完成
原理:
在主函數(shù)的while函數(shù)中,有一個(gè)停止的函數(shù),小車(chē)最開(kāi)始的狀態(tài)就是停止?fàn)顟B(tài),想讓小車(chē)開(kāi)始移動(dòng),就要打斷這個(gè)while循環(huán),所以就要用到中斷,所以點(diǎn)動(dòng)的代碼要在中斷函數(shù)中寫(xiě),所以在中斷回調(diào)函數(shù)中,有讓其移動(dòng)的代碼,將這個(gè)代碼延時(shí)一下,比如10ms。那么程序在執(zhí)行完移動(dòng)代碼之后就會(huì)執(zhí)行這個(gè)延時(shí)函數(shù),然后再回到while循環(huán)中執(zhí)行停止小車(chē)的函數(shù),所以就實(shí)現(xiàn)了,10ms的點(diǎn)動(dòng),而,需要長(zhǎng)時(shí)間的移動(dòng)小車(chē),就要在軟件中找補(bǔ)了。
在串口助手中
(把藍(lán)牙摘下去換上串口2,記得串口2的波特率已經(jīng)改為115200了)
串口助手2可以使用數(shù)據(jù)流循環(huán)發(fā)送,設(shè)置好發(fā)送的間隔,就可以實(shí)現(xiàn)長(zhǎng)時(shí)間的移動(dòng)。
設(shè)置的時(shí)間太長(zhǎng),沒(méi)辦法將小車(chē)動(dòng)作連續(xù)起來(lái)
時(shí)間太短,小車(chē)跑的太快
其實(shí)這個(gè)整套下來(lái),像是將片段的小車(chē)移動(dòng)整合起來(lái)變成一個(gè)連續(xù)的小車(chē)移動(dòng)
將小車(chē)獨(dú)立出來(lái)的接線:
現(xiàn)在小車(chē)的串口2連接藍(lán)牙模塊
ST-LINK和串口1可以在燒錄完代碼之后拔掉
32的5V電壓和GND可以連接面包板的正負(fù)極
面包板的電壓計(jì)算:四塊1.5V的電壓=1.5*4=6V
五、PWM調(diào)速控制小車(chē)
小車(chē)接收到不同的電壓值,跑的速度也是不一樣的
電壓越大,速度越快(并不是電壓越大越好,小心燒掉板子)
為了避免小車(chē)一開(kāi)始速度就很快可以使用PWM調(diào)速
小車(chē)的電機(jī)與32板子連接的電壓是3.3V,所以小車(chē)全速前進(jìn)的時(shí)候電壓是3.3V,如果把電壓降低至2V小車(chē)的速度就會(huì)降下來(lái),1.5V更慢。
復(fù)制01項(xiàng)目,重命名為04-PWM調(diào)速控制小車(chē)
復(fù)制呼吸燈實(shí)驗(yàn)中BSP文件中的pwm文件夾
粘貼到04-PWM調(diào)速控制小車(chē)的BSP文件中
打開(kāi)項(xiàng)目,加載文件
記得加載HAL庫(kù)中的定時(shí)器文件tim.c,tim_er.c
編譯無(wú)錯(cuò)
修改pwm文件
將這個(gè)的TIM4修改為T(mén)IM2,TIM2的兩個(gè)通道在這里用到
因?yàn)門(mén)IM4的兩個(gè)通道GPIO口可能會(huì)占用到
這里就要注意,做項(xiàng)目前要對(duì)資源進(jìn)行合理的分配,這很重要
這里需要更改電機(jī)的引腳
想要達(dá)成每個(gè)電機(jī)使用不同的通道
這里不用看PB引腳的通道,因?yàn)樽筝営靡粋€(gè)通道,右輪用一個(gè)通道,所以PB引腳的通道并不重要,不需要關(guān)注。
這里注意,通用定時(shí)器
代碼修改如下:
這里注意,原來(lái)我pwm_init函數(shù)中的參數(shù)是psc在前,arr在后,然后主函數(shù)中聲明pwm_init();時(shí)寫(xiě)的參數(shù)20-1,72-1,這里傳參的順序錯(cuò)了,所以在這里pwm文件中調(diào)換了psc和arr的順序。
復(fù)習(xí)CCR是什么?
主函數(shù)代碼如下:
產(chǎn)生的疑問(wèn)?
????????STM33F103C8T6智能小車(chē)項(xiàng)目中使用循跡+PWM調(diào)速小車(chē)時(shí),小車(chē)電機(jī)實(shí)際接線的引腳是PA0接A-1A、PB1接A-1B、PA1接B-1A、PB10接B-1B和PWM定義的接口引腳是PA0和PA1,但是在小車(chē)電機(jī)文件中定義的引腳是PB0接A-1A、PB1接A-1B、PB2 接B-1A、PB10接B-1B,這時(shí)候使用pwm調(diào)節(jié)CRR的值可以控制小車(chē)車(chē)速,但是為什么項(xiàng)目所定義的電機(jī)文件中的引腳和小車(chē)實(shí)際接線的引腳不一致的情況下,小車(chē)還可以動(dòng)?
DeepSeek回答
關(guān)于STM32F103C8T6智能小車(chē)項(xiàng)目中,電機(jī)實(shí)際接線與代碼中定義的引腳不一致,但小車(chē)仍然可以動(dòng)的原因。我需要仔細(xì)分析這個(gè)問(wèn)題,并給出合理的解釋。
首先,用戶(hù)提到實(shí)際接線是PA0接A-1A,PB1接A-1B,PA1接B-1A,PB10接B-1B。而代碼中定義的引腳是PB0接A-1A,PB1接A-1B,PB2接B-1A,PB10接B-1B。但使用PWM調(diào)節(jié)CCR(應(yīng)該是PWM的捕獲/比較寄存器)來(lái)控制車(chē)速,小車(chē)卻能動(dòng)。這看起來(lái)矛盾,因?yàn)橐_定義不一致,按理說(shuō)電機(jī)可能不會(huì)正常工作,或者完全不動(dòng)。
我需要考慮幾個(gè)可能性:
?硬件接線錯(cuò)誤,但代碼中錯(cuò)誤地配置了其他引腳?:比如,雖然代碼定義的是PB0,但實(shí)際可能錯(cuò)誤地配置了PA0,導(dǎo)致兩者巧合地對(duì)應(yīng)。這可能是因?yàn)榇a中的初始化部分錯(cuò)誤地將PB0配置為PA0,或者存在其他配置問(wèn)題。
?復(fù)用功能或引腳映射?:STM32的某些引腳可能有復(fù)用功能,或者通過(guò)重映射功能改變了實(shí)際控制的引腳。例如,定時(shí)器的通道可能被映射到不同的引腳,導(dǎo)致代碼中定義的PB0實(shí)際上輸出到了PA0。
?代碼中的引腳配置與實(shí)際不符,但部分引腳正確?:例如,雖然A-1A和B-1A的引腳定義錯(cuò)誤,但A-1B和B-1B的PB1和PB10正確,這樣可能有一半的電機(jī)驅(qū)動(dòng)正確,而另一半可能被其他因素(如共地或短路)導(dǎo)致電機(jī)仍能轉(zhuǎn)動(dòng),但表現(xiàn)異常。
?PWM信號(hào)通過(guò)其他方式傳遞?:可能存在飛線或電路板上的短路,導(dǎo)致PWM信號(hào)從代碼定義的引腳傳輸?shù)搅藢?shí)際接線的引腳。
接下來(lái),我需要進(jìn)一步分析這些可能性。首先檢查代碼中的GPIO初始化部分,查看是否正確配置了引腳。例如,如果代碼中錯(cuò)誤地將PB0配置為PWM輸出,但實(shí)際硬件連接到了PA0,但PA0可能被配置為其他功能(如普通GPIO輸出),這時(shí)候如果PA0被設(shè)置為高電平,可能電機(jī)一直全速運(yùn)行,而PWM調(diào)節(jié)無(wú)效。但用戶(hù)提到可以通過(guò)調(diào)節(jié)CCR控制車(chē)速,這說(shuō)明PWM可能確實(shí)在起作用。
另一種可能是,雖然代碼中定義的引腳是PB0、PB2,但實(shí)際使用的定時(shí)器通道對(duì)應(yīng)的引腳是PA0和PA1。例如,定時(shí)器2的通道1和2可能默認(rèn)映射到PA0和PA1,而代碼中錯(cuò)誤地配置了PB0和PB2,但由于沒(méi)有重映射,實(shí)際輸出仍然在PA0和PA1。這種情況下,即使代碼中試圖配置PB0和PB2為PWM,但由于這些引腳對(duì)應(yīng)的定時(shí)器通道不同,可能無(wú)法輸出,但用戶(hù)的情況卻可以工作,這似乎矛盾。
或者,用戶(hù)可能使用了不同的定時(shí)器,例如,PA0和PA1屬于定時(shí)器2,而PB0、PB1屬于定時(shí)器3,但代碼中錯(cuò)誤地配置了定時(shí)器3到PB0,但實(shí)際硬件連接到了定時(shí)器2的PA0,這時(shí)候如果代碼中錯(cuò)誤地配置了定時(shí)器3,但實(shí)際沒(méi)有啟用,而PA0可能被其他代碼(如直接GPIO控制)驅(qū)動(dòng),這可能無(wú)法解釋PWM調(diào)速的有效性。
可能需要考慮代碼中的具體配置,比如使用的定時(shí)器和通道,以及引腳重映射情況。例如,STM32的某些定時(shí)器通道可以重映射到其他引腳,如果代碼中啟用了重映射,那么實(shí)際輸出引腳會(huì)改變。例如,定時(shí)器3的通道1默認(rèn)是PA6,但部分重映射后可能是PB4,完全重映射可能是PC6。如果用戶(hù)代碼中配置了重映射,但實(shí)際硬件連接到了默認(rèn)引腳,或者相反,可能導(dǎo)致引腳不一致但信號(hào)仍然輸出。
另外,用戶(hù)提到調(diào)節(jié)CCR可以控制車(chē)速,這說(shuō)明PWM信號(hào)確實(shí)在正確的引腳上輸出。因此,可能的情況是,雖然代碼中定義的引腳名稱(chēng)和實(shí)際接線不一致,但實(shí)際配置的定時(shí)器通道對(duì)應(yīng)的物理引腳是正確的。例如,代碼中定義PB0,但實(shí)際配置的是定時(shí)器3通道1,而該通道的引腳可能是PB0,但硬件連接到了PA0,這似乎矛盾,除非存在硬件錯(cuò)誤或飛線。
或者,用戶(hù)可能混淆了引腳的定義,例如在代碼中錯(cuò)誤地命名了引腳,但實(shí)際配置的是正確的引腳。比如,代碼中將電機(jī)A的PWM引腳定義為PB0,但在初始化時(shí)實(shí)際配置的是PA0,這可能是代碼中的注釋或變量名錯(cuò)誤,而實(shí)際代碼正確。
另一個(gè)可能性是,電機(jī)驅(qū)動(dòng)模塊的使能端被固定啟用,例如ENA和ENB被接高電平,此時(shí)電機(jī)的方向由IN1和IN2控制,而PWM信號(hào)實(shí)際上控制的是速度,但若方向引腳配置正確,即使PWM引腳錯(cuò)誤地輸出高電平,電機(jī)也會(huì)全速運(yùn)轉(zhuǎn)。但用戶(hù)提到可以調(diào)節(jié)速度,所以PWM必須有效。
綜合來(lái)看,最可能的解釋是代碼中實(shí)際配置的引腳與變量名或注釋中的定義不一致,但初始化部分正確配置了實(shí)際接線的PA0和PA1。例如,雖然在代碼的注釋或變量名中寫(xiě)的是PB0和PB2,但初始化函數(shù)中正確地設(shè)置了PA0和PA1作為PWM輸出。這可能是一個(gè)代碼書(shū)寫(xiě)錯(cuò)誤,但實(shí)際配置正確,導(dǎo)致電機(jī)正常運(yùn)行。
或者,可能存在引腳復(fù)用的情況,例如PA0和PB0都被配置為同一個(gè)定時(shí)器的不同通道,但這種情況在STM32中不太可能,除非使用了不同的定時(shí)器。
需要進(jìn)一步考慮STM32F103的定時(shí)器通道和引腳映射。例如,查看PA0和PB0分別屬于哪個(gè)定時(shí)器的哪個(gè)通道。PA0對(duì)應(yīng)定時(shí)器2的通道1,而PB0可能屬于定時(shí)器3的通道3,或者其他定時(shí)器。如果代碼中配置了定時(shí)器2的通道1,那么無(wú)論變量名如何,實(shí)際輸出會(huì)在PA0,而如果變量名錯(cuò)誤地寫(xiě)成了PB0,但初始化代碼正確,那么電機(jī)就能正常工作。
因此,問(wèn)題可能出在代碼中的引腳定義變量名與實(shí)際初始化時(shí)使用的引腳不一致,但初始化部分正確配置了實(shí)際引腳。例如:
在代碼中可能有:
#define MOTOR_A_PWM_PIN PB0
#define MOTOR_B_PWM_PIN PB2
但在初始化時(shí),錯(cuò)誤地配置了PA0和PA1:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 實(shí)際是PA0
或者使用了正確的定時(shí)器配置,例如Timer2的通道1和2,對(duì)應(yīng)的PA0和PA1,而變量名錯(cuò)誤地寫(xiě)成了PB0和PB2。
因此,盡管代碼中的定義和實(shí)際接線不一致,但由于初始化部分正確配置了PA0和PA1,所以PWM信號(hào)正確輸出,電機(jī)能夠運(yùn)行,并且通過(guò)調(diào)節(jié)CCR(即定時(shí)器的捕獲/比較寄存器)改變占空比,從而控制速度。
總結(jié)起來(lái),可能的原因是代碼中引腳定義的變量名或注釋錯(cuò)誤,但實(shí)際的硬件初始化和定時(shí)器配置正確,導(dǎo)致雖然文檔或變量名不一致,但實(shí)際信號(hào)輸出正確,因此小車(chē)可以正常運(yùn)行。
📅 ?時(shí)效性分析?:用戶(hù)問(wèn)題不依賴(lài)時(shí)效性數(shù)據(jù),但為確保準(zhǔn)確性,當(dāng)前分析基于 ?2025年02月25日? 的技術(shù)背景。
🚗 ?問(wèn)題核心?
在引腳定義與實(shí)際接線不一致的情況下,小車(chē)仍能運(yùn)行的根本原因在于 ?代碼與硬件實(shí)際信號(hào)流的匹配性?。以下是關(guān)鍵分析:
🔧 ?原因解析?
1?? ?PWM信號(hào)實(shí)際生效的引腳與代碼定義不同?
?可能場(chǎng)景?:
代碼中雖然名義上定義引腳為 PB0 和 PB2,但底層驅(qū)動(dòng)(如定時(shí)器配置)可能錯(cuò)誤/隱式地指向了 PA0 和 PA1。
?示例?:
c
Copy Code
// 代碼中名義定義引腳為 PB0(錯(cuò)誤)
#define MOTOR_A_PWM_PIN PB0
// 實(shí)際初始化時(shí)誤配置為 PA0(正確)
TIM_OC1Init(TIM2, &pwm_config);? // TIM2_CH1 默認(rèn)映射到 PA0
?結(jié)果?:PWM信號(hào)通過(guò) PA0/PA1 輸出,與硬件接線一致,因此可調(diào)速。
2?? ?部分引腳功能重合或短路?
?可能場(chǎng)景?:
若 PB0 與 PA0 在硬件上被短接(如PCB設(shè)計(jì)錯(cuò)誤或飛線),代碼對(duì) PB0 的操作會(huì)實(shí)際影響 PA0。
部分電機(jī)驅(qū)動(dòng)模塊(如L298N)的使能端(ENA/ENB)直接接高電平,此時(shí)方向引腳(如A-1A/A-1B)的PWM占空比仍可調(diào)速。
3?? ?代碼未嚴(yán)格綁定引腳定義?
?可能場(chǎng)景?:
電機(jī)驅(qū)動(dòng)代碼通過(guò) ?定時(shí)器通道?(如 TIM2_CH1)控制PWM,而非直接依賴(lài)引腳名。
?示例?:
c
Copy Code
// 實(shí)際控制PA0的是TIM2_CH1,無(wú)論引腳名如何定義
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);? // PA0輸出PWM
📊 ?驗(yàn)證方法?
?示波器/邏輯分析儀檢測(cè)?:
檢查 PA0 和 PB0 的PWM波形,確認(rèn)實(shí)際生效的引腳。
?代碼調(diào)試?:
跟蹤PWM初始化代碼,驗(yàn)證定時(shí)器通道與引腳的映射關(guān)系(參考《STM32F103參考手冊(cè)》的定時(shí)器復(fù)用功能表)。
?硬件排查?:
檢查是否存在 PB0 與 PA0、PB2 與 PA1 的意外短路。
🛠? ?解決方案建議?
?統(tǒng)一代碼與硬件定義?:
修改代碼中的引腳定義,使其與實(shí)際接線一致(如將 PB0 改為 PA0)。
?明確定時(shí)器通道映射?:
在代碼中顯式配置引腳重映射(如需),避免隱式依賴(lài)默認(rèn)映射。
?示例?:
c
Copy Code
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);? // 顯式重映射TIM2通道
?添加注釋與文檔?:
在代碼和硬件手冊(cè)中明確記錄引腳對(duì)應(yīng)關(guān)系,減少后續(xù)維護(hù)成本。
? ?總結(jié)?
引腳定義不一致但小車(chē)能運(yùn)行,本質(zhì)是 ?信號(hào)流在硬件或代碼層被隱式修正?。需通過(guò)硬件檢測(cè)和代碼審計(jì)定位具體原因,最終確保定義與實(shí)物的嚴(yán)格一致。
六、循跡小車(chē)
循跡模塊詳解 | 良許嵌入式
注意這里的接線改回去了,要是不改回去會(huì)出錯(cuò),小車(chē)會(huì)不要電的一直跑
復(fù)制01重命名05-循跡小車(chē)
循跡模塊和紅外傳感器模塊很像,所以復(fù)制智能臺(tái)燈的ia_sensor
粘貼到05文件的BSP文件中,并且重命名為track
打開(kāi)文件
加載文件
七、循跡+PWM調(diào)速
??????? 上面循跡小車(chē)中,可能會(huì)出現(xiàn)由于地圖太小,小車(chē)太大,太沉,電機(jī)電壓夠足導(dǎo)致小車(chē)速度太快從而小車(chē)在拐彎的時(shí)候,沖出地圖跑道。
????????為了避免這這種情況的發(fā)生,可以選擇換一張大地圖、把小車(chē)重量減輕、體積減輕、讓小車(chē)變得更靈活,或者把小車(chē)電機(jī)電壓減少,小車(chē)速度放慢。
這里使用PWM調(diào)速小車(chē)
使用PWM的接線
復(fù)制04pwm文件重命名為06-循跡+pwm調(diào)速控制小車(chē)
將05中的track文件粘貼到06中
打開(kāi)工程,加載文件
這里注意改變arr和psc的值
因?yàn)樵瓉?lái)的20-1,72-1,步子太大了
上述中小車(chē)可以設(shè)置的pwm波為0-20,如果設(shè)置9太快了,如果設(shè)置8太慢了,所以讓它步子小一點(diǎn),這樣可以設(shè)置的值就更精確了
具體的數(shù)值,需要結(jié)合實(shí)際情況自己設(shè)定,這里95的速度只符合我這里
八、跟隨小車(chē)
跟隨小車(chē)要達(dá)到的效果是:
當(dāng)兩邊的跟隨模塊都檢測(cè)到東西的時(shí)候,就向前走,當(dāng)右邊檢測(cè)到東西的時(shí)候,就向右走,否則向左走,當(dāng)都未檢測(cè)到東西的時(shí)候,就停止。
復(fù)制01,重命名為07-跟隨小車(chē)
將循跡模塊的track重命名為followu
打開(kāi)項(xiàng)目,加載文件,將所有的track重命名為follow
更改引腳
代碼如下:
九、避障小車(chē)
復(fù)制項(xiàng)目文件01,重命名為08-避障小車(chē)
復(fù)制超聲波實(shí)驗(yàn)和舵機(jī)實(shí)驗(yàn)的文件,粘貼至如下圖所示
接線如圖所示:
打開(kāi)項(xiàng)目
加載文件
要加入定時(shí)器文件
舵機(jī)代碼可以分裝成左轉(zhuǎn),右轉(zhuǎn),中間,方便看
超聲波的代碼就是按照上面的接線,改一下引腳,就可以直接用了
主函數(shù)代碼如下:
#include "sys.h"
#include "delay.h"
#include "uart1.h"
#include "motor.h"
#include "sg90.h"
#include "hcsr04.h"int main(void)
{HAL_Init(); /* 初始化HAL庫(kù) */stm32_clock_init(RCC_PLL_MUL9); /* 設(shè)置時(shí)鐘, 72Mhz */uart1_init(115200);motor_init();sg90_init();hcsr04_init();printf("hello word!\r\n");double dis_left = 0, dis_middle = 0, dis_right = 0;sg90_turn_middle();while(1) { dis_middle = hcsr04_get_lenght();//接收超聲波所測(cè)的距離if(dis_middle > 35)//判斷距離是否大于35?是向前走motor_go_forward();else if(dis_middle < 15)//否則判斷距離是否小于15?是后退motor_go_backward();else //否則就是距離>15并且<35{motor_stop(); //先停下來(lái)//向左看并測(cè)量左側(cè)距離sg90_turn_left();delay_ms(500); //給個(gè)時(shí)間進(jìn)行測(cè)量dis_left = hcsr04_get_lenght(); //存儲(chǔ)下左側(cè)的長(zhǎng)度//居中sg90_turn_middle(); //超聲波傳感器回到中間的位置delay_ms(500);//向右看并測(cè)量右側(cè)距離sg90_turn_right();delay_ms(500);dis_right = hcsr04_get_lenght();//根據(jù)距離判斷左轉(zhuǎn)還是右轉(zhuǎn)if(dis_left < dis_right)motor_go_right();elsemotor_go_left();delay_ms(500); //給個(gè)時(shí)間反應(yīng)一下motor_stop(); //電機(jī)停一下sg90_turn_middle(); //舵機(jī)回到中間的位置}delay_ms(100); //給個(gè)時(shí)間反應(yīng)一下}
}
十、測(cè)速模塊
接線如下圖所示:
復(fù)制02-串口控制小車(chē)
重命名為09-測(cè)速小車(chē)
新建speed文件
打開(kāi)項(xiàng)目,加載文件
別忘了把定時(shí)器的驅(qū)動(dòng)文件加載進(jìn)來(lái)
寫(xiě)頭文件
注意:測(cè)速模塊要用到中斷,每次感受到一個(gè)下降沿就中斷一次,目的為了更好的測(cè)速
然后使用定時(shí)器,每一秒定時(shí)一次
代碼如下:
#include "speed.h"
#include "delay.h"
#include "stdio.h"uint16_t speed_cnt = 0;
TIM_HandleTypeDef timer_handle = {0};//定義結(jié)構(gòu)體//gpio口初始化
void speed_gpio_init(void)
{//使能GPIO時(shí)鐘GPIO_InitTypeDef gpio_initstruct;//定義一個(gè)結(jié)構(gòu)體gpio_initstruct//打開(kāi)時(shí)鐘-使能GPIO時(shí)鐘__HAL_RCC_GPIOB_CLK_ENABLE();//設(shè)置GPIO輸入模式//設(shè)置AFIO(開(kāi)啟時(shí)鐘,IO口映射)//設(shè)置EXTI(屏蔽,上/下沿)//調(diào)用GPIO初始化函數(shù)gpio_initstruct.Mode = GPIO_MODE_IT_FALLING;//下降沿輸出//結(jié)構(gòu)體函數(shù)的四種類(lèi)型,引腳模式、引腳、引腳上拉下拉、引腳速度gpio_initstruct.Pin = GPIO_PIN_12;//LED1對(duì)應(yīng)的引腳gpio_initstruct.Pull = GPIO_NOPULL;//上拉HAL_GPIO_Init(GPIOB,&gpio_initstruct);//設(shè)置NVIC(優(yōu)先級(jí)分組,設(shè)置優(yōu)先級(jí),使能中斷)HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,0);HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
//定時(shí)器中的初始化
void speed_timer_init(uint16_t arr,uint16_t psc)
{timer_handle.Instance = TIM2; //選擇使用的定時(shí)器timer_handle.Init.Prescaler = psc; //PSCtimer_handle.Init.Period = arr; //ARRtimer_handle.Init.CounterMode = TIM_COUNTERMODE_UP;//配置成向上的,默認(rèn)向上的timer_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //自動(dòng)重裝載,禁用自動(dòng)重裝載HAL_TIM_Base_Init(&timer_handle);HAL_TIM_Base_Start_IT(&timer_handle);//設(shè)置使能更新中斷,和啟動(dòng)計(jì)數(shù)器
}
//初始化speed函數(shù)
void speed_init(void)
{speed_gpio_init();speed_timer_init(10000-1,7200-1);//一秒中斷一次的定時(shí)器
}//設(shè)置中斷服務(wù)函數(shù)
void EXTI15_10_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}
//回調(diào)函數(shù)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{delay_ms(20);if(GPIO_Pin == GPIO_PIN_12){if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_12) == GPIO_PIN_RESET){speed_cnt++;printf("speed = %d\r\n",speed_cnt); //測(cè)量出格子的個(gè)數(shù)}}
}
//定時(shí)器msp函數(shù)
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){__HAL_RCC_TIM2_CLK_ENABLE();HAL_NVIC_SetPriority(TIM2_IRQn,2,2);//設(shè)置優(yōu)先級(jí)HAL_NVIC_EnableIRQ(TIM2_IRQn);//設(shè)置使能中斷}
}//定時(shí)器中斷服務(wù)函數(shù)
void TIM2_IRQHandler(void)
{//一般還會(huì)調(diào)用一個(gè)公共處理的服務(wù)函數(shù)HAL_TIM_IRQHandler(&timer_handle); }
//定時(shí)器中斷回調(diào)函數(shù)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){//測(cè)量到每一秒轉(zhuǎn)了多少個(gè)格子printf("speed = %d\r\n",speed_cnt);speed_cnt = 0;}
}
主函數(shù)如下:
#include "sys.h"
#include "delay.h"
#include "uart1.h"
#include "motor.h"
#include "bluetooth.h"
#include "speed.h"int main(void)
{HAL_Init(); /* 初始化HAL庫(kù) */stm32_clock_init(RCC_PLL_MUL9); /* 設(shè)置時(shí)鐘, 72Mhz */uart1_init(115200);motor_init();speed_init();bt_init(9600);printf("hello word!\r\n");motor_go_forward(); //讓小車(chē)前進(jìn)while(1) { }
}
十一、測(cè)速小車(chē)oled顯示并上傳藍(lán)牙
復(fù)制09,重命名為10-測(cè)速小車(chē)oled顯示并上傳藍(lán)牙
粘貼oled文件
打開(kāi)項(xiàng)目
加載文件
想把獲取到的速度,打印到oled上
我們得到的值是數(shù)字,要將這個(gè)數(shù)字轉(zhuǎn)換成字符串打印到oled屏幕上
數(shù)字轉(zhuǎn)字符串我們使用sprintf
定義一個(gè)字符數(shù)組
使用memset()先把這個(gè)字符數(shù)組清空,免得之后每次中斷之后傳進(jìn)來(lái)的數(shù)據(jù)對(duì)其進(jìn)行干擾
修改oled引腳
編譯,無(wú)錯(cuò)
將藍(lán)牙模塊的這個(gè)函數(shù)的這個(gè)位置修改成這樣
?因?yàn)樵瓉?lái)使用的sizeof打印子啊手機(jī)上間距太大,不正常,如下所示
所以,換成strlen,就變成了如下所示:
?十二、wifi控制小車(chē)速度
將藍(lán)牙模塊換成esp8266模塊
讓電腦可以看見(jiàn)小車(chē)的速度買(mǎi)也可以使用電腦控制小車(chē)前進(jìn)后退
將esp8266接到串口2 的tx和rx中
復(fù)制10,重命名11-WiFi控制小車(chē)并顯示速度
打開(kāi)項(xiàng)目,加載文件
將藍(lán)牙的相關(guān)文件刪除
編譯不出錯(cuò)
使用網(wǎng)絡(luò)調(diào)試助手架構(gòu)一個(gè)服務(wù)器
連接esp8266
?
#include "sys.h"
#include "delay.h"
#include "uart1.h"
#include "motor.h"
#include "speed.h"
#include "oled.h"
#include "esp8266.h"
#include "string.h"int main(void)
{HAL_Init(); /* 初始化HAL庫(kù) */stm32_clock_init(RCC_PLL_MUL9); /* 設(shè)置時(shí)鐘, 72Mhz */uart1_init(115200);motor_init();speed_init();oled_init();esp8266_init(115200); printf("hello word!\r\n");motor_go_forward(); //讓小車(chē)前進(jìn)char recv_data[ESP8266_RX_BUF_SIZE];while(1) { //esp8266發(fā)送數(shù)據(jù)esp8266_receive_data(recv_data);if(strstr(recv_data, "M1") != NULL)motor_go_forward();else if(strstr(recv_data, "M2") != NULL)motor_go_backward();else if(strstr(recv_data, "M3") != NULL)motor_go_left();else if(strstr(recv_data, "M4") != NULL)motor_go_right();elsemotor_stop();}
}
?
這里沒(méi)有用到空閑中斷,不把它注釋掉反而會(huì)出錯(cuò)
十三、4G控制小車(chē)
4G模塊引腳接線,tx、rx接串口2,vcc接串口1中的5v,gnd隨便找個(gè)地方接一下就可以
使用4G模塊要使用花生殼進(jìn)行內(nèi)網(wǎng)穿透,獲取外網(wǎng)地址
設(shè)置鏈路
對(duì)于4G模塊的代碼可以直接復(fù)用10-測(cè)速小車(chē)oled顯示并上傳藍(lán)牙
其中只需要注意4G模塊的波特率就好,你的4G模塊是什么波特率就改什么波特率
連接上4G模塊之后,
這段代碼就不是透?jìng)鞯剿{(lán)牙模塊上了,而是透?jìng)鞯?G模塊
所以就可以在網(wǎng)絡(luò)調(diào)試助手上看見(jiàn)了
十四、語(yǔ)音控制小車(chē)
接線如下圖所示:
配置SU-03T,點(diǎn)開(kāi)網(wǎng)站:智能公元/AI產(chǎn)品零代碼平臺(tái)
語(yǔ)言選擇中文,下一步
設(shè)置三次
?等待SDK燒錄完成之后,就可以下載固件了
將語(yǔ)音模塊接線之后
進(jìn)行燒錄
然后再燒錄的exe工具中,選擇如下文件
記住:這個(gè)文件不要放到中文路徑下,路徑也不要太長(zhǎng)
?
?至此,語(yǔ)音文件就燒錄完成了
下面是代碼部分的編寫(xiě):
復(fù)制01,重命名12-語(yǔ)音控制小車(chē)
?打開(kāi)項(xiàng)目,加載文件
記得加入定時(shí)器文件
編譯,無(wú)錯(cuò)之后進(jìn)行下一步
在主函數(shù)中寫(xiě)入頭文件和初始化函數(shù),編譯,無(wú)錯(cuò),進(jìn)行下一步
記得每次往主函數(shù)中融合其他模塊的時(shí)候,要編譯測(cè)試一下,減少錯(cuò)誤率
主函數(shù)代碼如下:
#include "sys.h"
#include "delay.h"
#include "uart1.h"
#include "motor.h"
#include "follow.h"
#include "hcsr04.h"
#include "sg90.h"
#include "track.h"double dis_left = 0, dis_middle = 0, dis_right = 0;//循跡模式
void track_mode(void)
{if(track_left_flag() == TRUE && track_right_flag() == TRUE)motor_go_forward(); //前進(jìn)else if(track_left_flag() == FALSE && track_right_flag() == TRUE) //左邊壓線motor_go_left(); //左轉(zhuǎn)else if(track_left_flag() == TRUE && track_right_flag() == FALSE) //右邊壓線motor_go_right(); //右轉(zhuǎn)else if(track_left_flag() == FALSE && track_right_flag() == FALSE) // 全部為黑色motor_stop(); //停止
}
//跟隨模式
void follow_mode(void)
{if(follow_left_flag() == TRUE && follow_right_flag() == TRUE)motor_go_forward();else if(follow_left_flag() == FALSE && follow_right_flag() == TRUE)//右邊有目標(biāo)motor_go_right();else if(follow_left_flag() == TRUE && follow_right_flag() == FALSE)//左邊有目標(biāo)motor_go_left();else if(follow_left_flag() == FALSE && follow_right_flag() == FALSE)motor_stop();
}
//避障模式
void avoid_mode(void)
{dis_middle = hcsr04_get_lenght();//接收超聲波所測(cè)的距離if(dis_middle > 35)//判斷距離是否大于35?是向前走motor_go_forward();else if(dis_middle < 15)//否則判斷距離是否小于15?是后退motor_go_backward();else //否則就是距離>15并且<35{motor_stop(); //先停下來(lái)//向左看并測(cè)量左側(cè)距離sg90_turn_left();delay_ms(500); //給個(gè)時(shí)間進(jìn)行測(cè)量dis_left = hcsr04_get_lenght(); //存儲(chǔ)下左側(cè)的長(zhǎng)度//居中sg90_turn_middle(); //超聲波傳感器回到中間的位置delay_ms(500);//向右看并測(cè)量右側(cè)距離sg90_turn_right();delay_ms(500);dis_right = hcsr04_get_lenght();//根據(jù)距離判斷左轉(zhuǎn)還是右轉(zhuǎn)if(dis_left < dis_right)motor_go_right();elsemotor_go_left();delay_ms(500); //給個(gè)時(shí)間反應(yīng)一下motor_stop(); //電機(jī)停一下sg90_turn_middle(); //舵機(jī)回到中間的位置}delay_ms(100); //給個(gè)時(shí)間反應(yīng)一下
}int main(void)
{HAL_Init(); /* 初始化HAL庫(kù) */stm32_clock_init(RCC_PLL_MUL9); /* 設(shè)置時(shí)鐘, 72Mhz */uart1_init(115200);motor_init();follow_init();hcsr04_init();sg90_init();track_init();printf("hello word!\r\n");sg90_turn_middle();while(1) { }
}
新建一個(gè)voice文件,加載文件
voice.c代碼如下:
voice.h文件
主函數(shù)代碼如下:
#include "sys.h"
#include "delay.h"
#include "uart1.h"
#include "motor.h"
#include "follow.h"
#include "hcsr04.h"
#include "sg90.h"
#include "track.h"
#include "voice.h"double dis_left = 0, dis_middle = 0, dis_right = 0;//循跡模式
void track_mode(void)
{if(track_left_flag() == TRUE && track_right_flag() == TRUE)motor_go_forward(); //前進(jìn)else if(track_left_flag() == FALSE && track_right_flag() == TRUE) //左邊壓線motor_go_left(); //左轉(zhuǎn)else if(track_left_flag() == TRUE && track_right_flag() == FALSE) //右邊壓線motor_go_right(); //右轉(zhuǎn)else if(track_left_flag() == FALSE && track_right_flag() == FALSE) // 全部為黑色motor_stop(); //停止
}
//跟隨模式
void follow_mode(void)
{if(follow_left_flag() == TRUE && follow_right_flag() == TRUE)motor_go_forward();else if(follow_left_flag() == FALSE && follow_right_flag() == TRUE)//右邊有目標(biāo)motor_go_right();else if(follow_left_flag() == TRUE && follow_right_flag() == FALSE)//左邊有目標(biāo)motor_go_left();else if(follow_left_flag() == FALSE && follow_right_flag() == FALSE)motor_stop();
}
//避障模式
void avoid_mode(void)
{dis_middle = hcsr04_get_lenght();//接收超聲波所測(cè)的距離if(dis_middle > 35)//判斷距離是否大于35?是向前走motor_go_forward();else if(dis_middle < 15)//否則判斷距離是否小于15?是后退motor_go_backward();else //否則就是距離>15并且<35{motor_stop(); //先停下來(lái)//向左看并測(cè)量左側(cè)距離sg90_turn_left();delay_ms(500); //給個(gè)時(shí)間進(jìn)行測(cè)量dis_left = hcsr04_get_lenght(); //存儲(chǔ)下左側(cè)的長(zhǎng)度//居中sg90_turn_middle(); //超聲波傳感器回到中間的位置delay_ms(500);//向右看并測(cè)量右側(cè)距離sg90_turn_right();delay_ms(500);dis_right = hcsr04_get_lenght();//根據(jù)距離判斷左轉(zhuǎn)還是右轉(zhuǎn)if(dis_left < dis_right)motor_go_right();elsemotor_go_left();delay_ms(500); //給個(gè)時(shí)間反應(yīng)一下motor_stop(); //電機(jī)停一下sg90_turn_middle(); //舵機(jī)回到中間的位置}delay_ms(100); //給個(gè)時(shí)間反應(yīng)一下
}int main(void)
{HAL_Init(); /* 初始化HAL庫(kù) */stm32_clock_init(RCC_PLL_MUL9); /* 設(shè)置時(shí)鐘, 72Mhz */uart1_init(115200);motor_init();follow_init();hcsr04_init();sg90_init();track_init();voice_init();printf("hello word!\r\n");sg90_turn_middle();while(1) { if(TRACK_PIN == GPIO_PIN_RESET && FOLLOW_PIN == GPIO_PIN_SET && AVOID_PIN == GPIO_PIN_SET)track_mode();//循跡模式else if(TRACK_PIN == GPIO_PIN_SET && FOLLOW_PIN == GPIO_PIN_RESET && AVOID_PIN == GPIO_PIN_SET)follow_mode();//跟隨模式else if(TRACK_PIN == GPIO_PIN_SET && FOLLOW_PIN == GPIO_PIN_SET && AVOID_PIN == GPIO_PIN_RESET)avoid_mode();//避障模式}
}
編譯無(wú)錯(cuò)