STM32-智能小車項目

項目框圖

ST-link接線

實物圖:

正面:

?反面:

相關內容

使用L9110S電機模塊

電機驅動模塊L9110S詳解 | 良許嵌入式

測速模塊

?

語音模塊SU-03T

網站:智能公元/AI產品零代碼平臺

一、讓小車動起來

新建文件夾智能小車項目

在里面復制19-串口打印功能

重命名為01-讓小車動起來

新建文件夾motor,如下圖所示

打開項目

加載文件

先更改引腳

然后分裝函數

前進,后退,左轉,右轉,停止函數

PB0、PB1引腳控制的是左輪:

當PB0引腳為高電平,PB1引腳為低電平的時候,左輪向前走

當PB0引腳為低電平,PB1引腳為高電平的時候,左輪向后走

當PB0引腳與PB1引腳電平相同的時候,左輪停止

PB2、PB10引腳控制的是右輪

當PB2引腳為高電平,PB10引腳為低電平的時候,右輪向前走

當PB2引腳為低電平,PB10引腳為高電平的時候,右輪向后走

當PB2引腳與PB10引腳電平相同的時候,右輪停止

#include "motor.h"//初始化電機函數
void motor_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打開時鐘__HAL_RCC_GPIOB_CLK_ENABLE();                           // 使能GPIOB時鐘//調用GPIO初始化函數gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10;          // 兩個LED對應的引腳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();
}
//前進
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);
}
//左轉
void motor_go_left(void)
{//左輪HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);//想要一個輪子不動,就讓它兩個引腳都等于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);
}
//右轉
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);
}

二、串口控制小車

注意:串口控制小車與藍牙控制小車類似

想要實現的效果是:

M1:前進

M2:后退

M3:左轉

M4:右轉

其他M:停止

復制01-讓小車動起來

重命名為02-串口控制小車

將藍牙模塊的文件粘貼到改項目中進行修改

打開項目文件

加載文件

將藍牙模塊的串口2代碼粘貼到02項目中

將接收到的串口2的數據打印到串口1中

編譯

調試代碼

燒錄

記得把串口2連接上,用兩個串口助手顯示結果

串口助手1->連接串口1->用于接收串口2發送過來的數據->波特率是115200

串口助手2->連接串口2->用于發送數據(M1\M2\M3\M4\其他數據)->波特率是9600

注意:在燒錄完代碼之后,一切準備就緒,發現在串口2中發送輸入,串口1接收不到,這時候不要慌,先把兩個串口助手斷開,然后再次選擇串口號,連接串口,這時候在輸入數據,就會有反應了。

注意:如果你的電腦中usb接口不足,板子選擇使用面包板接電就會出現一個小錯誤,面包板中只有一個3V3在供電,這個時候,使用杜邦線給32板子接電,就會出現在串口2中輸入M1前進之后再輸入M2后退,小車會停下來,串口助手1中會顯示hello word。如下圖所示:

再次輸入M2小車會后退,這是因為供電不足,電壓不穩,所以想要達到輸入M1就是前進,輸入M2就是后退,解決辦法:需要使用USB外接電源供電。

三、藍牙控制小車

將上述的串口控制小車中的串口2所接的引腳替換成藍牙模塊

然后手機連接藍牙

輸入數據,小車就會行動

要注意藍牙所設置的波特率,不然不能正確接收數據

藍牙當初所設置的波特率是115200,所以要在主函數中調用藍牙初始化函數時更改波特率

?

四、點動控制小車

????????當摁下前進按鍵的時候,小車就會瘋狂的向前走,那么現在要實現,當摁一下前進,小車就前進一下,實現點動控制小車。

復制項目文件02,重命名為,點動控制小車

打開項目

只添加這兩行代碼即可

剩下的在軟件上完成

原理:

在主函數的while函數中,有一個停止的函數,小車最開始的狀態就是停止狀態,想讓小車開始移動,就要打斷這個while循環,所以就要用到中斷,所以點動的代碼要在中斷函數中寫,所以在中斷回調函數中,有讓其移動的代碼,將這個代碼延時一下,比如10ms。那么程序在執行完移動代碼之后就會執行這個延時函數,然后再回到while循環中執行停止小車的函數,所以就實現了,10ms的點動,而,需要長時間的移動小車,就要在軟件中找補了。

在串口助手中

(把藍牙摘下去換上串口2,記得串口2的波特率已經改為115200了)

串口助手2可以使用數據流循環發送,設置好發送的間隔,就可以實現長時間的移動。

設置的時間太長,沒辦法將小車動作連續起來

時間太短,小車跑的太快

其實這個整套下來,像是將片段的小車移動整合起來變成一個連續的小車移動

將小車獨立出來的接線:

現在小車的串口2連接藍牙模塊

ST-LINK和串口1可以在燒錄完代碼之后拔掉

32的5V電壓和GND可以連接面包板的正負極

面包板的電壓計算:四塊1.5V的電壓=1.5*4=6V

五、PWM調速控制小車

小車接收到不同的電壓值,跑的速度也是不一樣的

電壓越大,速度越快(并不是電壓越大越好,小心燒掉板子)

為了避免小車一開始速度就很快可以使用PWM調速

小車的電機與32板子連接的電壓是3.3V,所以小車全速前進的時候電壓是3.3V,如果把電壓降低至2V小車的速度就會降下來,1.5V更慢。

復制01項目,重命名為04-PWM調速控制小車

復制呼吸燈實驗中BSP文件中的pwm文件夾

粘貼到04-PWM調速控制小車的BSP文件中

打開項目,加載文件

記得加載HAL庫中的定時器文件tim.c,tim_er.c

編譯無錯

修改pwm文件

將這個的TIM4修改為TIM2,TIM2的兩個通道在這里用到

因為TIM4的兩個通道GPIO口可能會占用到

這里就要注意,做項目前要對資源進行合理的分配,這很重要

這里需要更改電機的引腳

想要達成每個電機使用不同的通道

這里不用看PB引腳的通道,因為左輪用一個通道,右輪用一個通道,所以PB引腳的通道并不重要,不需要關注。

這里注意,通用定時器


代碼修改如下:

這里注意,原來我pwm_init函數中的參數是psc在前,arr在后,然后主函數中聲明pwm_init();時寫的參數20-1,72-1,這里傳參的順序錯了,所以在這里pwm文件中調換了psc和arr的順序。

復習CCR是什么?

主函數代碼如下:

產生的疑問?

????????STM33F103C8T6智能小車項目中使用循跡+PWM調速小車時,小車電機實際接線的引腳是PA0接A-1A、PB1接A-1B、PA1接B-1A、PB10接B-1B和PWM定義的接口引腳是PA0和PA1,但是在小車電機文件中定義的引腳是PB0接A-1A、PB1接A-1B、PB2 接B-1A、PB10接B-1B,這時候使用pwm調節CRR的值可以控制小車車速,但是為什么項目所定義的電機文件中的引腳和小車實際接線的引腳不一致的情況下,小車還可以動?

DeepSeek回答

關于STM32F103C8T6智能小車項目中,電機實際接線與代碼中定義的引腳不一致,但小車仍然可以動的原因。我需要仔細分析這個問題,并給出合理的解釋。

首先,用戶提到實際接線是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調節CCR(應該是PWM的捕獲/比較寄存器)來控制車速,小車卻能動。這看起來矛盾,因為引腳定義不一致,按理說電機可能不會正常工作,或者完全不動。

我需要考慮幾個可能性:

?硬件接線錯誤,但代碼中錯誤地配置了其他引腳?:比如,雖然代碼定義的是PB0,但實際可能錯誤地配置了PA0,導致兩者巧合地對應。這可能是因為代碼中的初始化部分錯誤地將PB0配置為PA0,或者存在其他配置問題。

?復用功能或引腳映射?:STM32的某些引腳可能有復用功能,或者通過重映射功能改變了實際控制的引腳。例如,定時器的通道可能被映射到不同的引腳,導致代碼中定義的PB0實際上輸出到了PA0。

?代碼中的引腳配置與實際不符,但部分引腳正確?:例如,雖然A-1A和B-1A的引腳定義錯誤,但A-1B和B-1B的PB1和PB10正確,這樣可能有一半的電機驅動正確,而另一半可能被其他因素(如共地或短路)導致電機仍能轉動,但表現異常。

?PWM信號通過其他方式傳遞?:可能存在飛線或電路板上的短路,導致PWM信號從代碼定義的引腳傳輸到了實際接線的引腳。

接下來,我需要進一步分析這些可能性。首先檢查代碼中的GPIO初始化部分,查看是否正確配置了引腳。例如,如果代碼中錯誤地將PB0配置為PWM輸出,但實際硬件連接到了PA0,但PA0可能被配置為其他功能(如普通GPIO輸出),這時候如果PA0被設置為高電平,可能電機一直全速運行,而PWM調節無效。但用戶提到可以通過調節CCR控制車速,這說明PWM可能確實在起作用。

另一種可能是,雖然代碼中定義的引腳是PB0、PB2,但實際使用的定時器通道對應的引腳是PA0和PA1。例如,定時器2的通道1和2可能默認映射到PA0和PA1,而代碼中錯誤地配置了PB0和PB2,但由于沒有重映射,實際輸出仍然在PA0和PA1。這種情況下,即使代碼中試圖配置PB0和PB2為PWM,但由于這些引腳對應的定時器通道不同,可能無法輸出,但用戶的情況卻可以工作,這似乎矛盾。

或者,用戶可能使用了不同的定時器,例如,PA0和PA1屬于定時器2,而PB0、PB1屬于定時器3,但代碼中錯誤地配置了定時器3到PB0,但實際硬件連接到了定時器2的PA0,這時候如果代碼中錯誤地配置了定時器3,但實際沒有啟用,而PA0可能被其他代碼(如直接GPIO控制)驅動,這可能無法解釋PWM調速的有效性。

可能需要考慮代碼中的具體配置,比如使用的定時器和通道,以及引腳重映射情況。例如,STM32的某些定時器通道可以重映射到其他引腳,如果代碼中啟用了重映射,那么實際輸出引腳會改變。例如,定時器3的通道1默認是PA6,但部分重映射后可能是PB4,完全重映射可能是PC6。如果用戶代碼中配置了重映射,但實際硬件連接到了默認引腳,或者相反,可能導致引腳不一致但信號仍然輸出。

另外,用戶提到調節CCR可以控制車速,這說明PWM信號確實在正確的引腳上輸出。因此,可能的情況是,雖然代碼中定義的引腳名稱和實際接線不一致,但實際配置的定時器通道對應的物理引腳是正確的。例如,代碼中定義PB0,但實際配置的是定時器3通道1,而該通道的引腳可能是PB0,但硬件連接到了PA0,這似乎矛盾,除非存在硬件錯誤或飛線。

或者,用戶可能混淆了引腳的定義,例如在代碼中錯誤地命名了引腳,但實際配置的是正確的引腳。比如,代碼中將電機A的PWM引腳定義為PB0,但在初始化時實際配置的是PA0,這可能是代碼中的注釋或變量名錯誤,而實際代碼正確。

另一個可能性是,電機驅動模塊的使能端被固定啟用,例如ENA和ENB被接高電平,此時電機的方向由IN1和IN2控制,而PWM信號實際上控制的是速度,但若方向引腳配置正確,即使PWM引腳錯誤地輸出高電平,電機也會全速運轉。但用戶提到可以調節速度,所以PWM必須有效。

綜合來看,最可能的解釋是代碼中實際配置的引腳與變量名或注釋中的定義不一致,但初始化部分正確配置了實際接線的PA0和PA1。例如,雖然在代碼的注釋或變量名中寫的是PB0和PB2,但初始化函數中正確地設置了PA0和PA1作為PWM輸出。這可能是一個代碼書寫錯誤,但實際配置正確,導致電機正常運行。

或者,可能存在引腳復用的情況,例如PA0和PB0都被配置為同一個定時器的不同通道,但這種情況在STM32中不太可能,除非使用了不同的定時器。

需要進一步考慮STM32F103的定時器通道和引腳映射。例如,查看PA0和PB0分別屬于哪個定時器的哪個通道。PA0對應定時器2的通道1,而PB0可能屬于定時器3的通道3,或者其他定時器。如果代碼中配置了定時器2的通道1,那么無論變量名如何,實際輸出會在PA0,而如果變量名錯誤地寫成了PB0,但初始化代碼正確,那么電機就能正常工作。

因此,問題可能出在代碼中的引腳定義變量名與實際初始化時使用的引腳不一致,但初始化部分正確配置了實際引腳。例如:

在代碼中可能有:
#define MOTOR_A_PWM_PIN PB0
#define MOTOR_B_PWM_PIN PB2

但在初始化時,錯誤地配置了PA0和PA1:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 實際是PA0
或者使用了正確的定時器配置,例如Timer2的通道1和2,對應的PA0和PA1,而變量名錯誤地寫成了PB0和PB2。

因此,盡管代碼中的定義和實際接線不一致,但由于初始化部分正確配置了PA0和PA1,所以PWM信號正確輸出,電機能夠運行,并且通過調節CCR(即定時器的捕獲/比較寄存器)改變占空比,從而控制速度。

總結起來,可能的原因是代碼中引腳定義的變量名或注釋錯誤,但實際的硬件初始化和定時器配置正確,導致雖然文檔或變量名不一致,但實際信號輸出正確,因此小車可以正常運行。

📅 ?時效性分析?:用戶問題不依賴時效性數據,但為確保準確性,當前分析基于 ?2025年02月25日? 的技術背景。

🚗 ?問題核心?

在引腳定義與實際接線不一致的情況下,小車仍能運行的根本原因在于 ?代碼與硬件實際信號流的匹配性?。以下是關鍵分析:

🔧 ?原因解析?
1?? ?PWM信號實際生效的引腳與代碼定義不同?
?可能場景?:
代碼中雖然名義上定義引腳為 PB0 和 PB2,但底層驅動(如定時器配置)可能錯誤/隱式地指向了 PA0 和 PA1。
?示例?:
c
Copy Code
// 代碼中名義定義引腳為 PB0(錯誤)
#define MOTOR_A_PWM_PIN PB0
// 實際初始化時誤配置為 PA0(正確)
TIM_OC1Init(TIM2, &pwm_config);? // TIM2_CH1 默認映射到 PA0

?結果?:PWM信號通過 PA0/PA1 輸出,與硬件接線一致,因此可調速。
2?? ?部分引腳功能重合或短路?
?可能場景?:
若 PB0 與 PA0 在硬件上被短接(如PCB設計錯誤或飛線),代碼對 PB0 的操作會實際影響 PA0。
部分電機驅動模塊(如L298N)的使能端(ENA/ENB)直接接高電平,此時方向引腳(如A-1A/A-1B)的PWM占空比仍可調速。
3?? ?代碼未嚴格綁定引腳定義?
?可能場景?:
電機驅動代碼通過 ?定時器通道?(如 TIM2_CH1)控制PWM,而非直接依賴引腳名。
?示例?:
c
Copy Code
// 實際控制PA0的是TIM2_CH1,無論引腳名如何定義
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);? // PA0輸出PWM

📊 ?驗證方法?
?示波器/邏輯分析儀檢測?:
檢查 PA0 和 PB0 的PWM波形,確認實際生效的引腳。
?代碼調試?:
跟蹤PWM初始化代碼,驗證定時器通道與引腳的映射關系(參考《STM32F103參考手冊》的定時器復用功能表)。
?硬件排查?:
檢查是否存在 PB0 與 PA0、PB2 與 PA1 的意外短路。
🛠? ?解決方案建議?
?統一代碼與硬件定義?:
修改代碼中的引腳定義,使其與實際接線一致(如將 PB0 改為 PA0)。
?明確定時器通道映射?:
在代碼中顯式配置引腳重映射(如需),避免隱式依賴默認映射。
?示例?:
c
Copy Code
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);? // 顯式重映射TIM2通道

?添加注釋與文檔?:
在代碼和硬件手冊中明確記錄引腳對應關系,減少后續維護成本。
? ?總結?

引腳定義不一致但小車能運行,本質是 ?信號流在硬件或代碼層被隱式修正?。需通過硬件檢測和代碼審計定位具體原因,最終確保定義與實物的嚴格一致。

六、循跡小車

循跡模塊詳解 | 良許嵌入式

注意這里的接線改回去了,要是不改回去會出錯,小車會不要電的一直跑

復制01重命名05-循跡小車

循跡模塊和紅外傳感器模塊很像,所以復制智能臺燈的ia_sensor

粘貼到05文件的BSP文件中,并且重命名為track

打開文件

加載文件

七、循跡+PWM調速

??????? 上面循跡小車中,可能會出現由于地圖太小,小車太大,太沉,電機電壓夠足導致小車速度太快從而小車在拐彎的時候,沖出地圖跑道。

????????為了避免這這種情況的發生,可以選擇換一張大地圖、把小車重量減輕、體積減輕、讓小車變得更靈活,或者把小車電機電壓減少,小車速度放慢。

這里使用PWM調速小車

使用PWM的接線

復制04pwm文件重命名為06-循跡+pwm調速控制小車

將05中的track文件粘貼到06中

打開工程,加載文件

這里注意改變arr和psc的值

因為原來的20-1,72-1,步子太大了

上述中小車可以設置的pwm波為0-20,如果設置9太快了,如果設置8太慢了,所以讓它步子小一點,這樣可以設置的值就更精確了

具體的數值,需要結合實際情況自己設定,這里95的速度只符合我這里

八、跟隨小車

跟隨小車要達到的效果是:

當兩邊的跟隨模塊都檢測到東西的時候,就向前走,當右邊檢測到東西的時候,就向右走,否則向左走,當都未檢測到東西的時候,就停止。

復制01,重命名為07-跟隨小車

將循跡模塊的track重命名為followu

打開項目,加載文件,將所有的track重命名為follow

更改引腳

代碼如下:

九、避障小車

復制項目文件01,重命名為08-避障小車

復制超聲波實驗和舵機實驗的文件,粘貼至如下圖所示

接線如圖所示:

打開項目

加載文件

要加入定時器文件

舵機代碼可以分裝成左轉,右轉,中間,方便看

超聲波的代碼就是按照上面的接線,改一下引腳,就可以直接用了

主函數代碼如下:

#include "sys.h"
#include "delay.h"
#include "uart1.h"
#include "motor.h"
#include "sg90.h"
#include "hcsr04.h"int main(void)
{HAL_Init();                         /* 初始化HAL庫 */stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 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();//接收超聲波所測的距離if(dis_middle > 35)//判斷距離是否大于35?是向前走motor_go_forward();else if(dis_middle < 15)//否則判斷距離是否小于15?是后退motor_go_backward();else //否則就是距離>15并且<35{motor_stop();  //先停下來//向左看并測量左側距離sg90_turn_left();delay_ms(500);  //給個時間進行測量dis_left = hcsr04_get_lenght();   //存儲下左側的長度//居中sg90_turn_middle();   //超聲波傳感器回到中間的位置delay_ms(500);//向右看并測量右側距離sg90_turn_right();delay_ms(500);dis_right = hcsr04_get_lenght();//根據距離判斷左轉還是右轉if(dis_left < dis_right)motor_go_right();elsemotor_go_left();delay_ms(500);  //給個時間反應一下motor_stop();  //電機停一下sg90_turn_middle();  //舵機回到中間的位置}delay_ms(100);  //給個時間反應一下}
}

十、測速模塊

接線如下圖所示:

復制02-串口控制小車

重命名為09-測速小車

新建speed文件

打開項目,加載文件

別忘了把定時器的驅動文件加載進來

寫頭文件

注意:測速模塊要用到中斷,每次感受到一個下降沿就中斷一次,目的為了更好的測速

然后使用定時器,每一秒定時一次

代碼如下:

#include "speed.h"
#include "delay.h"
#include "stdio.h"uint16_t speed_cnt = 0;
TIM_HandleTypeDef timer_handle = {0};//定義結構體//gpio口初始化
void speed_gpio_init(void)
{//使能GPIO時鐘GPIO_InitTypeDef gpio_initstruct;//定義一個結構體gpio_initstruct//打開時鐘-使能GPIO時鐘__HAL_RCC_GPIOB_CLK_ENABLE();//設置GPIO輸入模式//設置AFIO(開啟時鐘,IO口映射)//設置EXTI(屏蔽,上/下沿)//調用GPIO初始化函數gpio_initstruct.Mode = GPIO_MODE_IT_FALLING;//下降沿輸出//結構體函數的四種類型,引腳模式、引腳、引腳上拉下拉、引腳速度gpio_initstruct.Pin = GPIO_PIN_12;//LED1對應的引腳gpio_initstruct.Pull = GPIO_NOPULL;//上拉HAL_GPIO_Init(GPIOB,&gpio_initstruct);//設置NVIC(優先級分組,設置優先級,使能中斷)HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,0);HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
//定時器中的初始化
void speed_timer_init(uint16_t arr,uint16_t psc)
{timer_handle.Instance = TIM2; //選擇使用的定時器timer_handle.Init.Prescaler = psc; //PSCtimer_handle.Init.Period = arr; //ARRtimer_handle.Init.CounterMode = TIM_COUNTERMODE_UP;//配置成向上的,默認向上的timer_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;  //自動重裝載,禁用自動重裝載HAL_TIM_Base_Init(&timer_handle);HAL_TIM_Base_Start_IT(&timer_handle);//設置使能更新中斷,和啟動計數器
}
//初始化speed函數
void speed_init(void)
{speed_gpio_init();speed_timer_init(10000-1,7200-1);//一秒中斷一次的定時器
}//設置中斷服務函數
void EXTI15_10_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}
//回調函數
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);  //測量出格子的個數}}
}
//定時器msp函數
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){__HAL_RCC_TIM2_CLK_ENABLE();HAL_NVIC_SetPriority(TIM2_IRQn,2,2);//設置優先級HAL_NVIC_EnableIRQ(TIM2_IRQn);//設置使能中斷}
}//定時器中斷服務函數 
void TIM2_IRQHandler(void)
{//一般還會調用一個公共處理的服務函數HAL_TIM_IRQHandler(&timer_handle); }
//定時器中斷回調函數
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){//測量到每一秒轉了多少個格子printf("speed = %d\r\n",speed_cnt);speed_cnt = 0;}
}

主函數如下:

#include "sys.h"
#include "delay.h"
#include "uart1.h"
#include "motor.h"
#include "bluetooth.h"
#include "speed.h"int main(void)
{HAL_Init();                         /* 初始化HAL庫 */stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 72Mhz */uart1_init(115200);motor_init();speed_init();bt_init(9600);printf("hello word!\r\n");motor_go_forward(); //讓小車前進while(1) { }
}

十一、測速小車oled顯示并上傳藍牙

復制09,重命名為10-測速小車oled顯示并上傳藍牙

粘貼oled文件

打開項目

加載文件

想把獲取到的速度,打印到oled上

我們得到的值是數字,要將這個數字轉換成字符串打印到oled屏幕上

數字轉字符串我們使用sprintf

定義一個字符數組

使用memset()先把這個字符數組清空,免得之后每次中斷之后傳進來的數據對其進行干擾

修改oled引腳

編譯,無錯

將藍牙模塊的這個函數的這個位置修改成這樣

?因為原來使用的sizeof打印子啊手機上間距太大,不正常,如下所示

所以,換成strlen,就變成了如下所示:

?十二、wifi控制小車速度

將藍牙模塊換成esp8266模塊

讓電腦可以看見小車的速度買也可以使用電腦控制小車前進后退

將esp8266接到串口2 的tx和rx中

復制10,重命名11-WiFi控制小車并顯示速度

打開項目,加載文件

將藍牙的相關文件刪除

編譯不出錯

使用網絡調試助手架構一個服務器

連接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庫 */stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 72Mhz */uart1_init(115200);motor_init();speed_init();oled_init();esp8266_init(115200); printf("hello word!\r\n");motor_go_forward(); //讓小車前進char recv_data[ESP8266_RX_BUF_SIZE];while(1) { //esp8266發送數據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();}
}

?

這里沒有用到空閑中斷,不把它注釋掉反而會出錯

十三、4G控制小車

4G模塊引腳接線,tx、rx接串口2,vcc接串口1中的5v,gnd隨便找個地方接一下就可以

使用4G模塊要使用花生殼進行內網穿透,獲取外網地址

設置鏈路

對于4G模塊的代碼可以直接復用10-測速小車oled顯示并上傳藍牙

其中只需要注意4G模塊的波特率就好,你的4G模塊是什么波特率就改什么波特率

連接上4G模塊之后,

這段代碼就不是透傳到藍牙模塊上了,而是透傳到4G模塊

所以就可以在網絡調試助手上看見了

十四、語音控制小車

接線如下圖所示:

配置SU-03T,點開網站:智能公元/AI產品零代碼平臺

語言選擇中文,下一步

設置三次

?等待SDK燒錄完成之后,就可以下載固件了

將語音模塊接線之后

進行燒錄

然后再燒錄的exe工具中,選擇如下文件

記住:這個文件不要放到中文路徑下,路徑也不要太長

?

?至此,語音文件就燒錄完成了

下面是代碼部分的編寫:

復制01,重命名12-語音控制小車

?打開項目,加載文件

記得加入定時器文件

編譯,無錯之后進行下一步

在主函數中寫入頭文件和初始化函數,編譯,無錯,進行下一步

記得每次往主函數中融合其他模塊的時候,要編譯測試一下,減少錯誤率

主函數代碼如下:

#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(); //前進else if(track_left_flag() == FALSE && track_right_flag() == TRUE)  //左邊壓線motor_go_left(); //左轉else if(track_left_flag() == TRUE && track_right_flag() == FALSE)  //右邊壓線motor_go_right(); //右轉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)//右邊有目標motor_go_right();else if(follow_left_flag() == TRUE && follow_right_flag() == FALSE)//左邊有目標motor_go_left();else if(follow_left_flag() == FALSE && follow_right_flag() == FALSE)motor_stop();
}
//避障模式
void avoid_mode(void)
{dis_middle = hcsr04_get_lenght();//接收超聲波所測的距離if(dis_middle > 35)//判斷距離是否大于35?是向前走motor_go_forward();else if(dis_middle < 15)//否則判斷距離是否小于15?是后退motor_go_backward();else //否則就是距離>15并且<35{motor_stop();  //先停下來//向左看并測量左側距離sg90_turn_left();delay_ms(500);  //給個時間進行測量dis_left = hcsr04_get_lenght();   //存儲下左側的長度//居中sg90_turn_middle();   //超聲波傳感器回到中間的位置delay_ms(500);//向右看并測量右側距離sg90_turn_right();delay_ms(500);dis_right = hcsr04_get_lenght();//根據距離判斷左轉還是右轉if(dis_left < dis_right)motor_go_right();elsemotor_go_left();delay_ms(500);  //給個時間反應一下motor_stop();  //電機停一下sg90_turn_middle();  //舵機回到中間的位置}delay_ms(100);  //給個時間反應一下
}int main(void)
{HAL_Init();                         /* 初始化HAL庫 */stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 72Mhz */uart1_init(115200);motor_init();follow_init();hcsr04_init();sg90_init();track_init();printf("hello word!\r\n");sg90_turn_middle();while(1) { }
}

新建一個voice文件,加載文件

voice.c代碼如下:

voice.h文件

主函數代碼如下:

#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(); //前進else if(track_left_flag() == FALSE && track_right_flag() == TRUE)  //左邊壓線motor_go_left(); //左轉else if(track_left_flag() == TRUE && track_right_flag() == FALSE)  //右邊壓線motor_go_right(); //右轉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)//右邊有目標motor_go_right();else if(follow_left_flag() == TRUE && follow_right_flag() == FALSE)//左邊有目標motor_go_left();else if(follow_left_flag() == FALSE && follow_right_flag() == FALSE)motor_stop();
}
//避障模式
void avoid_mode(void)
{dis_middle = hcsr04_get_lenght();//接收超聲波所測的距離if(dis_middle > 35)//判斷距離是否大于35?是向前走motor_go_forward();else if(dis_middle < 15)//否則判斷距離是否小于15?是后退motor_go_backward();else //否則就是距離>15并且<35{motor_stop();  //先停下來//向左看并測量左側距離sg90_turn_left();delay_ms(500);  //給個時間進行測量dis_left = hcsr04_get_lenght();   //存儲下左側的長度//居中sg90_turn_middle();   //超聲波傳感器回到中間的位置delay_ms(500);//向右看并測量右側距離sg90_turn_right();delay_ms(500);dis_right = hcsr04_get_lenght();//根據距離判斷左轉還是右轉if(dis_left < dis_right)motor_go_right();elsemotor_go_left();delay_ms(500);  //給個時間反應一下motor_stop();  //電機停一下sg90_turn_middle();  //舵機回到中間的位置}delay_ms(100);  //給個時間反應一下
}int main(void)
{HAL_Init();                         /* 初始化HAL庫 */stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 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();//避障模式}
}

編譯無錯

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/70860.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/70860.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/70860.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Linux】vim 設置

【Linux】vim 設置 零、起因 剛學Linux&#xff0c;有時候會重裝Linux系統&#xff0c;然后默認的vi不太好用&#xff0c;需要進行一些設置&#xff0c;本文簡述如何配置一個好用的vim。 壹、軟件安裝 sudo apt-get install vim貳、配置路徑 對所有用戶生效&#xff1a; …

【Python爬蟲(90)】以Python爬蟲為眼,洞察金融科技監管風云

【Python爬蟲】專欄簡介&#xff1a;本專欄是 Python 爬蟲領域的集大成之作&#xff0c;共 100 章節。從 Python 基礎語法、爬蟲入門知識講起&#xff0c;深入探討反爬蟲、多線程、分布式等進階技術。以大量實例為支撐&#xff0c;覆蓋網頁、圖片、音頻等各類數據爬取&#xff…

【java進階】java多態深入探討

前言 在Java的編程宇宙中,多態是極為關鍵的概念,它宛如一條靈動的紐帶,串聯起面向對象編程的諸多特性,賦予程序宛如生命般的動態活力與高度靈活性。透徹理解多態,不僅是提升代碼質量的關鍵,更是開啟高效編程大門的鑰匙。 一、多態的定義與本質 多態,從概念層面來講,…

17164字符遷移

17164字符遷移 ??難度&#xff1a;中等 &#x1f4d6; &#x1f4da; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt();int q scanner.nextInt();scanner.next…

強化學習概覽

強化學習的目標 智能體&#xff08;Agent&#xff09;通過與環境&#xff08;Environment&#xff09;交互&#xff0c;學習最大化累積獎勵&#xff08;Cumulative Reward&#xff09;?的策略。 數學抽象 馬爾科夫決策過程&#xff08;MDP&#xff09; 收益 由于馬爾科夫決…

IDEA關閉SpringBoot程序后仍然占用端口的排查與解決

IDEA關閉SpringBoot程序后仍然占用端口的排查與解決 問題描述 在使用 IntelliJ IDEA 開發 Spring Boot 應用時&#xff0c;有時即使關閉了應用&#xff0c;程序仍然占用端口&#xff08;例如&#xff1a;4001 端口&#xff09;。這會導致重新啟動應用時出現端口被占用的錯誤&a…

QT:QPen、QBrush、與圖形抗鋸齒的關聯

QPen QPen 是 Qt 框架中用于定義繪圖時使用的畫筆屬性的類。在使用 QPainter 進行 2D 繪圖時&#xff0c;QPen 可以控制線條的外觀&#xff0c;比如線條的顏色、寬度、樣式&#xff08;如實線、虛線等&#xff09;、端點樣式&#xff08;如方形端點、圓形端點等&#xff09;和…

論文筆記(七十二)Reward Centering(三)

Reward Centering&#xff08;三&#xff09; 文章概括摘要3 基于值的獎勵中心化4 案例研究&#xff1a; 以獎勵為中心的 Q-learning5 討論、局限性與未來工作致謝 文章概括 引用&#xff1a; article{naik2024reward,title{Reward Centering},author{Naik, Abhishek and Wan…

單例模式——c++

一個類&#xff0c;只能有1個對象 (對象在堆空間) 再次創建該對象&#xff0c;直接引用之前的對象 so構造函數不能隨意調用 so構造函數私有 so對象不能構造 如何調用私有化的構造函數: 公開接口調用構造函數 調用構造函數&#xff1a;singleTon instance&#xff1b; 但…

ReentrantLock 底層實現

一、核心概念 1 - CAS CAS&#xff08;Compare-And-Swap&#xff0c;比較并交換&#xff09;操作是一種無鎖的原子操作&#xff0c;它在多線程環境下能夠保證線程安全&#xff0c;主要是通過硬件級別的原子性以及樂觀鎖的思想來實現的。以下詳細介紹 CAS 操作保證線程安全的原…

NLP學習記錄十:多頭注意力

一、單頭注意力 單頭注意力的大致流程如下&#xff1a; ① 查詢編碼向量、鍵編碼向量和值編碼向量分別經過自己的全連接層&#xff08;Wq、Wk、Wv&#xff09;后得到查詢Q、鍵K和值V&#xff1b; ② 查詢Q和鍵K經過注意力評分函數&#xff08;如&#xff1a;縮放點積運算&am…

算法-二叉樹篇08-完全二叉樹的節點個數

完全二叉樹的節點個數 力扣題目鏈接 題目描述 給你一棵 完全二叉樹 的根節點 root &#xff0c;求出該樹的節點個數。 完全二叉樹 的定義如下&#xff1a;在完全二叉樹中&#xff0c;除了最底層節點可能沒填滿外&#xff0c;其余每層節點數都達到最大值&#xff0c;并且最下…

【原創工具】同文件夾PDF文件合并 By憐渠客

【原創工具】同文件夾PDF文件合并 By憐渠客 原貼&#xff1a;可批量合并多個文件夾內的pdf工具 - 吾愛破解 - 52pojie.cn 他這個存在一些問題&#xff0c;并非是軟件內自主實現的PDF合并&#xff0c;而是調用的pdftk這一工具&#xff0c;但樓主并沒有提供pdftk&#xff0c;而…

微軟云和金山云和k8有什么區別

Kubernetes&#xff08;K8s&#xff09;和微軟云&#xff08;Microsoft Cloud&#xff09;是兩種不同的技術&#xff0c;分別用于不同的目的。Kubernetes是一個開源的容器編排系統&#xff0c;用于自動化部署、擴展和管理容器化應用程序&#xff0c;而微軟云是一個提供多種云服…

libGL.so.1: cannot open shared object file: No such file or directory-linux022

in <module> from PyQt5.QtGui import QPixmap, QFont, QIcon ImportError: libGL.so.1: cannot open shared object file: No such file or directory 這個錯誤信息表示XXXX 在啟動時遇到問題&#xff0c;缺少 libGL.so.1 文件。libGL.so.1 是與 OpenGL 圖形庫相關的共…

滲透測試【seacms V9】

搭建seacms環境 我選擇在虛擬機中用寶塔搭建環境 將在官網選擇的下載下來的文件解壓后拖入寶塔面板的文件中 創建網站 添加站點 搭建完成seacmsV9 找到一個報錯口 代碼分析 <?php set_time_limit(0); error_reporting(0); $verMsg V6.x UTF8; $s_lang utf-8; $dfDbn…

論文閱讀筆記:Continual Forgetting for Pre-trained Vision Models

論文閱讀筆記&#xff1a;Continual Forgetting for Pre-trained Vision Models 1 背景2 創新點3 方法4 模塊4.1 問題設置4.2 LoRA4.3 概述4.4 GS-LoRA4.5 損失函數 5 效果6 結論 1 背景 出于隱私和安全考慮&#xff0c;如今從預先訓練的視覺模型中刪除不需要的信息的需求越來…

車載DoIP診斷框架 --- 連接 DoIP ECU/車輛的故障排除

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 簡單,單純,喜歡獨處,獨來獨往,不易合同頻過著接地氣的生活,除了生存溫飽問題之外,沒有什么過多的欲望,表面看起來很高冷,內心熱情,如果你身…

【爬蟲基礎】第二部分 爬蟲基礎理論 P1/3

上節內容回顧&#xff1a;【爬蟲基礎】第一部分 網絡通訊 P1/3-CSDN博客 【爬蟲基礎】第一部分 網絡通訊-Socket套接字 P2/3-CSDN博客 【爬蟲基礎】第一部分 網絡通訊-編程 P3/3-CSDN博客 爬蟲相關文檔&#xff0c;希望互相學習&#xff0c;共同進步 風123456789&#xff…

Compose 手勢處理,增進交互體驗

Compose 手勢處理&#xff0c;增進交互體驗 概述常用手勢處理Modifierclickable()combinedClickable()draggable()swipeable()transformable()scrollable()nestedScrollNestedScrollConnectionNestedScrollDispatcher 定制手勢處理使用 PointerInput ModifierPointerInputScope…