stm32项目_stm32f103c8t6项目_循迹避障小车完整制作过程_智能小车设计_STM32智能小车教程-循迹-避障-蓝牙遥控-跟随
[硬件]
元件選型
照片上傳出問題了,改天補上,著急的可以看視頻
視頻鏈接在這里
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-sKjJGMXv-1646313963955)(…/…/…/…/MyBlogGitee/blog/source/imagesSTM32%E5%B0%8F%E8%BD%A6%E7%AC%94%E8%AE%B0V1.0/image-20220303131457462.png)]
原理圖繪制
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-4Y1vYGWJ-1646313963957)(…/…/…/…/MyBlogGitee/blog/source/imagesSTM32%E5%B0%8F%E8%BD%A6%E7%AC%94%E8%AE%B0V1.0/image-20220303131554546.png)]
要結合購買的元件模塊設計原理圖
比如
查看數據手冊與參考手冊確定引腳功能
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-kDAUGJ9n-1646313963957)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220208194421919.png)]
PCB布局與走線
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-bnDQ4kBN-1646313963958)(…/…/…/…/MyBlogGitee/blog/source/imagesSTM32%E5%B0%8F%E8%BD%A6%E7%AC%94%E8%AE%B0V1.0/image-20220303132333282.png)]
電源線走線粗一點
可以把電源線走在底層,信號線在頂層
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Ch7UqHRV-1646313963959)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220208210628764.png)]
根據元件特點布局
核心板的排母間距要注意!!!
比如:這種元件就要放到PCB邊上
下單PCB打樣
檢查DRC沒有問題就可以打樣了
焊接PCB
焊接比較簡單
如果大家有問題,留言我抽空補上視頻
安裝組裝
安裝比較簡單
如果大家有問題,留言我抽空補上視頻
[軟件]編程開發中如何獲得資料
模塊資料
我們通過淘寶獲得
STM32F103C8T6最小系統板模塊
通過淘寶下載同一型號資料即可
其他模塊資料
可以通過淘寶簡介得到
STM32外設驅動資料
我們通過正點原子下載獲取:
小車原理圖
通過EDA軟件導出
程序移植-STM32F103ZET6移植到STM32F103C8T6
第一步
打開魔術棒,點擊Device,更改芯片類型為C8T6
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-N3ALbwms-1646313963959)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113173242935.png)]
第二步
點擊Target,晶振頻率改為8Mhz
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LJJFUsmS-1646313963960)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113173436660.png)]
第三步
點擊C/C++,將define中的STM32F10X_HD,USE_STDPERIPH_DRIVER改成STM32F10X_MD,USE_STDPERIPH_DRIVER
STM32F10X_MD,USE_STDPERIPH_DRIVER
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LMLZGuGq-1646313963960)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113173653028.png)]
第四步
點擊Utilities,點開settings,在Flash Download欄下,將STM32F103ZET6中512k的移除,并改為128k,
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2jzyDeIb-1646313963961)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113174002136.png)]
第五步
將該工程文件中CORE中的startup_stm32f10x_hd.s文件換為startup_stm32f10x_md.s文件
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-4BbjxCZc-1646313963961)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113183940843.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XdU25Kou-1646313963962)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113184742797.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DILgDGz8-1646313963962)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113192246006.png)]
第六步
編譯一下
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oRbc6YlL-1646313963963)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113192519150.png)]
那么我們就完成了把ZET6的工程移植成為C8T6的工作,下面讓我們點燈測試一下啊.
使用STlink燒錄 時候出現:
方法:
GPIO輸出實驗點亮C8T6板載小燈
第一步
查閱原理圖,小燈接在PC13上下面驅動PC13
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jeFGlgSG-1646313963963)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113195307685.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FUvVvhyX-1646313963963)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113195226345.png)]
思考題:如果同時驅動PC13與PC14,應該如何編寫?(答案:應該增加下圖代碼)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-j9NIgtkw-1646313963964)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113195611097.png)]
LED_Init()函數的代碼
void LED_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PC端口時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //PC13GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHzGPIO_Init(GPIOC, &GPIO_InitStructure); //根據設定參數初始化GPIOC.13GPIO_SetBits(GPIOC,GPIO_Pin_13); //PC.13輸出高 }LED.h 部分宏定義
#define LED PCout(13)// PC13第二步
編譯下載(如果沒有運行,需要按復位 運行)
以上我們就完成基本測試,下面讓我們學習一下,如何從零設計小車!!!<( ̄︶ ̄)↗[GO!]
小車設計
總體設計方案
總體的設計方案對完成項目非常重要,下面是小車的設計方案,
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jJgjG0yf-1646313963964)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113215641227.png)]
對小車的模組進行了,簡單分類。這里簡單介紹一下:
一輛’自動化’小車,要能夠像人一樣,有觀察事物的眼睛,有處理事情的大腦,有可以跑動的腿,這里:
輸入信號模塊就像人類的眼睛,可以講一些外界信息測量并送至’大腦’,比如超聲波把距離信息發送給單片機。
執行模塊就像人類的腿,可以根據’大腦’控制指令進行’運動’,比如舵機根據單片機指令旋轉。
單片機就像人類的大腦,可以根據輸入信號模塊完成對執行模塊的控制。
電源負責給整個系統供電。
OLED模塊顯示一些系統信息。
[硬件]系統硬件設計
主控:STM32單片機
使用:STM32f103c8t6最小系統板
選擇原因:STM32F103C8T6價格較低,資源豐富可以滿足項目要求,可以在其數據手冊閱讀資源介紹。
注意:
系統需要5V供電,可輸出3.3V
OLED模塊
使用:OLED顯示屏模塊 0.96寸 IIC/SPI
選擇原因:價格較低、使用方便
注意:
這里使用 四管腳 順序為 GND VCC SCL SDA,繪制PCB要注意順序
供電為3.3V
陀螺儀
使用:MPU-6050模塊 三軸加速度陀螺儀6DOF GY-521
原因:滿足項目需要,使用方便
注意:
供電3V-5V
超聲波測距模塊
使用:HC-SR04 超聲波測距模塊
注意:
繪制PCB注意四個引腳順序 Vcc Trig Echo Gnd
供電3.3V-5V
測距原理
不同模式
GPIO模式
紅外循跡模塊
使用:尋跡傳感器 TCRT5000紅外反射傳感器
注意:
供電3.3V-5V
引腳順序為: VCC GDN DO AO (DO表示數字輸出,AO表示模擬輸出)
來自TB的介紹
不完全總結就是:紅外對管前面是黑色的時候,DO引腳為高電平,二極管熄滅狀態。前面是紅色的時候為低電平,二極管點亮。
藍牙模塊
使用:HC-05 主從機一體藍牙串口透傳模塊
注意:
供電3.6V-6V
引腳順序 VCC GND TXD RXD
按鍵
使用:這里按鍵使用PCB 元件
電機驅動
使用:TB6612FNG電機驅動模塊
注意:
供電 比較復雜
來自淘寶的介紹
電機
使用:電機馬達 DC3V-6V直流減速電機
注意:
供電3V-6V
電機要能夠安裝在小車車架上(這里使用的電機是小車車架套餐配套的)
舵機
使用:SG90 9g舵機 固定翼航模遙控飛機 180度舵機
注意:
供電4.8V-6V
需要控制角度,故購買180度 舵機
電源
使用:12v鋰電池組18650充電帶保護板大容量電瓶通用移動電源便攜蓄電池
注意:
使用電池輸出為12V
接口為DC5.5-2.1公母頭
系統軟件設計
點亮小燈
查看原理圖
查閱原理圖,小燈接在PC13上下面驅動PC13
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LNU6Z0Mb-1646313963965)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113195307685.png)]
編寫驅動
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-60KaVxby-1646313963966)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113195226345.png)]
思考題:如果同時驅動PC13與PC14,應該如何編寫?(答案:應該增加下圖代碼)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KRdMp3nk-1646313963966)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220113195611097.png)]
LED_Init()函數的代碼
void LED_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PC端口時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //PC13GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHzGPIO_Init(GPIOC, &GPIO_InitStructure); //根據設定參數初始化GPIOC.13GPIO_SetBits(GPIOC,GPIO_Pin_13); //PC.13輸出高 }LED.h 部分宏定義
#define LED PCout(13)// PC13測試
編譯下載(如果沒有運行,需要按復位 運行)
電機驅動
由TB6612介紹得,通過控制AO和AO2高低電平可以控制AIN1和AIN2輸出。
GPIO 高低電平控制AIN和BIN
原理同GPIO輸出高低電平見第二節
TB6612 GPIO驅動函數代碼
//驅動6612 的AIN1 AIN2 BIN1 BIN2 // AIN1 PB13 // AIN2 PB12 // BIN1 PB1 // BIN2 PB0 void TB6612_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口時鐘 GPIO_InitStructure.GPIO_Pin =GPIO_Pin_13 |GPIO_Pin_12|GPIO_Pin_0|GPIO_Pin_1; //PB0 OB1 PB12 PB13端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHzGPIO_Init(GPIOB, &GPIO_InitStructure); //根據設定參數初始化GPIO_SetBits(GPIOB,GPIO_Pin_13 |GPIO_Pin_12|GPIO_Pin_0|GPIO_Pin_1); //PB0 OB1 PB12 PB1 輸出高}相關宏定義
#define AIN1 PBout(13)// PB13 #define AIN2 PBout(12)// PB12 #define BIN1 PBout(1)// PB1 #define BIN2 PBout(0)// PB0PWM控制PWMA和PWMB
將 PWM輸出實驗 的 timer 文件移植到我們前面點燈的工程中,更改驅動文件
查看原理圖 PWMA 和PWMB依次連接PA11和PA8
查看 參考手冊 關于定時器復用功能重映射的介紹(中文參考手冊第119頁)
初始化外設
-
配置對應引腳功能
-
初始化TIM1
-
初始化TIM1 相應通道的 PWM模式
-
使能
-
注意輸出使能 高級定時器必須使用:TIM_CtrlPWMOutputs(TIM_TypeDef TIMx, FunctionalState NewState);*
調用初始化函數、改變占空比。
TIM1_PWM_Init(1999,359); //TIM1掛在APB2為72M ,故計算 72 000 000 /(359+1)/(1999+1) = 100 Hz,//故設置了頻率為100 Hz、自動重裝載值 1999TIM_SetCompare1(TIM1,100); //設置 TIM1 通道1 捕獲/比較寄存器值 為 1000 可以計算出占空比//PA8 PWMBTIM_SetCompare4(TIM1,1900); //設置 //PA11 PWMA通過軟件仿真
邏輯分析儀觀察波形輸出、顯示PWM波形
設置好仿真環境
打開邏輯分析儀
添加要觀察的引腳
跳到設置對應程序位置,打開仿真
打開實時更新選項
調節觀察分析儀
產生的如圖方波就是一種PWM波
那么在程序哪里設置的這些參數那
時鐘預分頻數 決定了PWM 頻率和周期
TIM1_PWM_Init(1999,359); //TIM1掛在APB2為72M ,故計算 72 000 000 /(359+1)/(1999+1) = 100 Hz,那么誰調節占空比那?
非常好理解、定時器的計數器向上計數就是越來越大。
PWM 模式我們可以看手冊
3.這里的TIM_OCPolarity_High 就是把有效電平設置為高
舉個栗子:如果我們設置上面的示例參數,工作過程應該是怎么的吶?
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-EVjjm9v6-1646313963967)(C:\Users\z1930\AppData\Roaming\Typora\typora-user-images\image-20220201141825559.png)]
電機控制通過AIN1、AIN2、BIN1、BIN2控制電機正反轉,通過PWMA、PWMB控制電機轉速。
AIN1 = 1;AIN2 = 0;BIN1 = 1;BIN2 = 0;TIM_SetCompare4(TIM1,1500); //設置 ATIM_SetCompare1(TIM1,1500); //設置B讓小車跑一跑吧
小車電機線正確接法
錯誤接法
小車直行
void Forward(void) {AIN1 = 1;AIN2 = 0;BIN1 = 1;BIN2 = 0;TIM_SetCompare4(TIM1,1500); //設置 ATIM_SetCompare1(TIM1,1500); //設置B }小車后退
void Backward(void) {AIN1 = 0;AIN2 = 1;BIN1 = 0;BIN2 = 1;TIM_SetCompare4(TIM1,1500); //設置 ATIM_SetCompare1(TIM1,1500); //設置B }小車左轉
void Leftward(void) {AIN1 = 0;AIN2 = 1;BIN1 = 1;BIN2 = 0;TIM_SetCompare4(TIM1,1500); //設置 ATIM_SetCompare1(TIM1,1500); //設置B }小車右轉
void Rightward(void) {AIN1 = 1;AIN2 = 0;BIN1 = 0;BIN2 = 1;TIM_SetCompare4(TIM1,1500); //設置 ATIM_SetCompare1(TIM1,1500); //設置B}舵機控制
查看原理圖
芯片手冊
使用上節移植的定時器三例程
不需要開啟部分重映射,
//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5初始化函數為
//TIM3 PWM部分初始化 //PWM輸出初始化 //arr:自動重裝值 //psc:時鐘預分頻數 void TIM3_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;//使能對應時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定時器3時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外設和AFIO復用功能模塊時鐘//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5 //設置該引腳為復用輸出功能,輸出TIM3 CH1的PWM脈沖波形 GPIOA.6GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //TIM_CH1GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO//初始化TIM1TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作為TIMx時鐘頻率除數的預分頻值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位//初始化TIM3 Channel1 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時器模式:TIM脈沖寬度調制模式1TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根據T指定的參數初始化外設TIM3 OC1TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的預裝載寄存器TIM_Cmd(TIM3, ENABLE); //使能TIM3}讓舵機搖搖頭
調用初始化函數和改變占空比
TIM3_PWM_Init(999,1439); //時鐘源為72MHZ 故72 000 000 /(1439+1)/(999+1)=50HZTIM_SetCompare1(TIM3,32); //舵機向右delay_ms(900);TIM_SetCompare1(TIM3,80); //舵機向前delay_ms(900);TIM_SetCompare1(TIM3,130); //舵機向左 delay_ms(900);然后
按鍵與紅外對管
按鍵外部中斷實驗
讓我們先實現按鍵控制燈的亮滅
查看原理圖
這里發現翻車,嗚嗚嗚
由于C8T6小板子的PA12接了上拉電阻,所以使用PA12的時候要注意。而且如果我們用Mrico USB供電可能會影響PA11。
現在我們的原理圖是這樣的 KEY1-PA7 KEY2-PA12
配置按鍵端口模式
通過原理圖知:KEY1(PA7)應該配置成下拉輸入、上升沿觸發。
KEY2(PA12)應該配置成上拉輸入、下降沿觸發。
//按鍵初始化函數 void KEY_Init(void) //IO初始化 { GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PORTA時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA7設置成輸入,默認下拉 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.7GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //設置成上拉輸入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.12}配置中斷線和配置外部通道
//KEY外部中斷服務程序 void KEY_EXTIX_Init(void) {EXTI_InitTypeDef EXTI_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;KEY_Init(); // 按鍵端口初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能復用功能時鐘//GPIOA.7 中斷線以及中斷初始化配置 上升沿觸發 PA7 KEY_1GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource7); //選擇GPIO引腳作為中斷線EXTI_InitStructure.EXTI_Line=EXTI_Line7; //線路選擇 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //事件選擇 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //觸發模式 上升沿觸發EXTI_Init(&EXTI_InitStructure); //根據EXTI_InitStruct中指定的參數初始化外設EXTI寄存器//GPIOA.5 中斷線以及中斷初始化配置 下降沿觸發PA12 KEY_2GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource12);EXTI_InitStructure.EXTI_Line=EXTI_Line12;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿觸發EXTI_Init(&EXTI_InitStructure); //根據EXTI_InitStruct中指定的參數初始化外設EXTI寄存器NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //使能按鍵KEY1所在的外部中斷通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優先級2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子優先級1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的參數初始化外設NVIC寄存器NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //使能按鍵KEY0所在的外部中斷通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優先級2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; //子優先級0 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的參數初始化外設NVIC寄存器 }相關宏定義 讀取按鍵狀態
#define KEY_1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)//讀取按鍵KEY_1 #define KEY_2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_12)//讀取按鍵KEY_2編寫響應中斷函數
void EXTI9_5_IRQHandler(void)//按鍵KEY_1 和KEY_2的中斷服務函數 {delay_ms(10);//消抖if(KEY_1 == 1) //判斷按鍵KEY_1 是否被按下{LED =! LED;EXTI_ClearITPendingBit(EXTI_Line7); //清除LINE7上的中斷標志位 }} void EXTI15_10_IRQHandler(void)//按鍵KEY_SW1 和KEY_SW2的中斷服務函數 {delay_ms(10);//消抖if(KEY_2 == 0) //判斷按鍵KEY_2 是否被按下{LED =! LED;EXTI_ClearITPendingBit(EXTI_Line12); //清除LINE7上的中斷標志位 } }調用初始化函數
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶占優先級,2位響應優先級 //如果沒有設置中斷優先級分組要先設置 KEY_EXTIX_Init(); //初始化外部中斷輸入燒錄調試
觀察現象
紅外對管硬件使用方法
詳見:系統硬件設計->紅外循跡模塊
可以把紅外對管看成’按鍵’,當前面有黑色時候為高電平,前面白色低電平。
紅外對管的驅動
紅外對管這里使用查詢的方式,通過GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)函數獲得對應端口的電平
查看原理圖 紅外對管依次連接 PB5 、PB4 、PB3 、PA15
紅外管GPIO初始化
注意:這里我們需要使用的PB3、PB4、PA15是單片機的’特殊引腳
我們打開數據手冊:STM32F103x8B_DS_CH_V10,在引腳定義章節,說明了復位后的主功能和默認復用功能以及重定義功能。
在參考手冊:STM32中文參考手冊_V10, 在8.3.5 JTAG/SWD復用功能重映射中,說明了引腳使用
所以我們需要關閉JTAG-DP 啟用SW-DP ,我們重映射配置應寫為GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
//紅外循跡TCRT5000初始化函數 void TCRT5000_Init(void) //IO初始化 { GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);//使能PORTA,PORTB時鐘GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//重映射配置關閉JTAG-DP 啟用SW-DP從而可以使用PA15 PB3 PB4GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA15 設置成下拉輸入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.15GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_4|GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 設置成下拉輸入GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB 5 4 3 }一些宏定義,利用函數讀取電平
#define HW_1 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5)//讀取 PB5 電平 #define HW_2 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4)//讀取 PB4 #define HW_3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3)//讀取 PB3 #define HW_4 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15)//讀取 PA15調用初始化函數
TCRT5000_Init();紅外對管控制小燈
while(1){if(HW_1 == 1 && HW_2==0 && HW_3 == 1 && HW_4 == 0 )//當第一個和第三個前面是黑色時候板子小燈亮,其他情況板子小燈滅{ LED =0;}else{ LED =1;}}練一練–紅外對管循跡
while(1){if(HW_1 == 0 && HW_2 == 0 && HW_3 == 0 && HW_4 == 0){Forward();delay_ms(50);}if(HW_1 == 0 && HW_2 == 1 && HW_3 == 0 && HW_4 == 0){Rightward();delay_ms(150);}if(HW_1 == 1 && HW_2 == 0 && HW_3 == 0 && HW_4 == 0){Rightward();delay_ms(250);}if(HW_1 == 1 && HW_2 == 1 && HW_3 == 0 && HW_4 == 0){Rightward();delay_ms(300);}if(HW_1 == 0 && HW_2 == 0 && HW_3 == 1 && HW_4 == 0){Leftward();delay_ms(150);}if(HW_1 == 0 && HW_2 == 0 && HW_3 == 0 && HW_4 == 1){Leftward();delay_ms(250);}if(HW_1 == 0 && HW_2 == 0 && HW_3 == 1 && HW_4 == 1){Leftward();delay_ms(300);}}串口接收發送
STM32串口初始化
這里先初始化使用串口1
//串口1中斷服務程序 //注意,讀取USARTx->SR能避免莫名其妙的錯誤 u8 USART_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節. //接收狀態 //bit15, 接收完成標志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字節數目 u16 USART_RX_STA=0; //接收狀態標記 void uart_init(u32 bound){//GPIO端口設置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘//USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優先級3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器//USART 初始化設置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式USART_Init(USART1, &USART_InitStructure); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串口接受中斷USART_Cmd(USART1, ENABLE); //使能串口1 }在main中定義標志位
int g_USART1_FLAG1 = 0; //串口控制標志位
在usart.h中聲明變量
extern int g_USART1_FLAG1 ;
在中斷服務函數添加處理
void USART1_IRQHandler(void) //串口1中斷服務程序{u8 Res; #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.OSIntEnter(); #endifif(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾){Res =USART_ReceiveData(USART1); //讀取接收到的數據if(Res == 'A') g_USART1_FLAG1 = 1 ; //根據接受的數據 置為標志位if(Res == 'B')g_USART1_FLAG1 = 2 ;if((USART_RX_STA&0x8000)==0)//接收未完成{if(USART_RX_STA&0x4000)//接收到了0x0d{if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始else USART_RX_STA|=0x8000; //接收完成了 }else //還沒收到0X0D{ if(Res==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收 } }} } #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.OSIntExit(); #endif }調用初始化函數
uart_init(115200); //串口初始化為115200
在main.c 的邏輯
while(1){ //串口 if(g_USART1_FLAG1 == 1){ LED =! LED; }if(g_USART3_FLAG1 == 2) {LED =! LED; } }測試單片機串口
TTL與單片機連接
TTL插入電腦,使用串口助手->選擇端口->更改波特率115200->發送數據
現象 發送A 或B 可以使小燈反轉、發送其他命令無現象。
配置藍牙
更改藍牙波特率
見硬件藍牙介紹
我們在AT模式下設置發送AT指令:AT+UART=115200,0,0
測試藍牙
斷電重啟藍牙,更改軟件波特率為115200,打開手機藍牙與HC-05配對 (密碼:1234)
使用藍牙調試器(應用商店下載即可),發送aa 觀察電腦串口軟件
手機APP-藍牙調試器的設置方法
調試成功 :藍牙軟件和串口軟件能夠通訊
練一練–藍牙控制小燈
連接如圖
通過發送A或者B 控制單片機小燈反轉
那么上面我們就完成了藍牙的基本控制
然后我們就可以藍牙反轉燈的時候控制小車前行停止
//串口 if(g_USART1_FLAG1 == 1){g_USART1_FLAG1 = 0;//左電機慢速正轉 AIN1=0;AIN2=1;TIM_SetCompare4(TIM1,1700); //設置//右邊電機慢速執行BIN1 =1;BIN2 =0;TIM_SetCompare1(TIM1,1700); LED =! LED; }if(g_USART1_FLAG1 == 2) {g_USART1_FLAG1 = 0; //雙電機停止BIN1 = 0;BIN2 = 0;AIN1 = 0;AIN2 =0;LED =! LED; }上面是通過串口一(PA9 PA10)
藍牙硬件是串口三(PB10 PB11)下面我們通過串口三實現
初始化使用串口3
//初始化串口3 void uart_init_3(u32 bound){//GPIO端口設置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //使能USART3RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //,GPIOB時鐘//USART3_TX GPIOB.10GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB.10GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.10//USART3_RX GPIOB.11初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PB11GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.11 //Usart3 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優先級3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器//USART 初始化設置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式USART_Init(USART3, &USART_InitStructure); //初始化串口3USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//開啟串口接受中斷USART_Cmd(USART3, ENABLE); //使能串口3 }在main中定義標志位
int g_USART3_FLAG1 = 0; //串口3控制標志位
在usart.h中聲明變量
extern int g_USART3_FLAG1 ;
在中斷服務函數添加處理
//串口3 中斷處理函數 void USART3_IRQHandler (void) {u8 Res; if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {Res =USART_ReceiveData(USART3); //讀取接收到的數據if(Res == 'A') g_USART3_FLAG1 = 1 ; //根據接受的數據 置為標志位if(Res == 'B')g_USART3_FLAG1 = 2 ;} }調用初始化函數
uart_init_3(115200); //初始化串口3
在main.c 編寫邏輯
while(1){ //串口 if(g_USART3_FLAG1 == 1){ g_USART3_FLAG1 = 0;//左電機慢速正轉 AIN1=0;AIN2=1;TIM_SetCompare4(TIM1,1700); //設置//右邊電機慢速執行BIN1 =1;BIN2 =0;TIM_SetCompare1(TIM1,1700); LED =! LED; }if(g_USART3_FLAG1 == 2) {g_USART3_FLAG1 = 0; //雙電機停止BIN1 = 0;BIN2 = 0;AIN1 = 0;AIN2 =0;LED =! LED; } }把藍牙安裝順序連接到STM32
跳線帽改至藍牙
手機連接藍牙 使用藍牙調試器發送 A 或者 B
現象:發送A 小車直行、發送B小車停止。
練一練–藍牙控制小車運動
USART中斷服務函數
//串口3 中斷處理函數 void USART3_IRQHandler (void) {u8 Res; if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {Res =USART_ReceiveData(USART3); //讀取接收到的數據if(Res == 'A') g_USART3_FLAG1 = 1 ; //根據接受的數據 置為標志位if(Res == 'B')g_USART3_FLAG1 = 2 ;if(Res == 'C') g_USART3_FLAG1 = 3 ; //根據接受的數據 置為標志位if(Res == 'D')g_USART3_FLAG1 = 4 ;if(Res == 'E')g_USART3_FLAG1 = 5; }}main 中的邏輯
while(1){if(g_USART3_FLAG1 == 1) //前進{g_USART3_FLAG1=0;Forward();delay_ms(500);}if(g_USART3_FLAG1 == 2) //向右{g_USART3_FLAG1=0;Rightward();delay_ms(500);}if(g_USART3_FLAG1 ==3) //向左{g_USART3_FLAG1=0;Leftward();delay_ms(500);}if(g_USART3_FLAG1 ==4) //向后{g_USART3_FLAG1=0;Backward();delay_ms(500);}if(g_USART3_FLAG1 ==5) //停止{g_USART3_FLAG1=0;AIN1=0;AIN2=0;BIN1=0;BIN2=0;delay_ms(500);}}手機中藍牙調試助手的設計
練一練–把數據發送給電腦串口助手和手機APP
前面我們介紹了,如何通過電腦或者藍牙APP,向單片機發送數據,下面我們介紹如何:單片機如何向電腦和藍牙APP發送數據。
庫函數提供了相關串口函數,但是每次只能發送一個字節
USART_SendData(USART1,'X');//通過庫函數發送字節數據 while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//判斷發送標志位,是否發送結束在正點原子例程中完成了對printf的重映射,所以我們可以輕松的通過printf ()函數向串口1 發送不定長數據,這是正點原子的例程
struct __FILE { int handle; }; FILE __stdout; //定義_sys_exit()以避免使用半主機模式 void _sys_exit(int x) { x = x; } //重定義fputc函數 int fputc(int ch, FILE *f) { while((USART1->SR&0X40)==0);//循環發送,直到發送完畢 通過SR寄存器判斷是否發送完成 USART1->DR = (u8) ch; //通過DR寄存器發送數據return ch; }那么我們如何實現任意串口都可以任性發送那?
這里我們使用vsprintf 格式化字符串來完成
需要包含的頭文件
#include "stdarg.h" void UsartPrintf(USART_TypeDef * USARTx,char * fmt ,...) {unsigned char UsartPrintfBuf[256]; //定義一個字符串數組va_list ap;//初始化指向參數列表的指針unsigned char *pStr = UsartPrintfBuf; //指針指向數組首地址va_start(ap,fmt);//將第一個可變參數的地址付給ap,即ap 指向可變參數列表的開始vsprintf((char *)UsartPrintfBuf, fmt,ap);//將參數fmt、ap 指向的可變參數一起轉化成格式化字符串,放string數組中,作用同sprintf(),只是參數類型不同 va_end(ap); //清除指針while(*pStr != 0) //判斷是否發送完字符串{//while(USART_GetFlagStatus(USART3,USART_FLAG_TC == RESET));//判斷發送標志位,是否發送結束USART_SendData(USARTx,*pStr++);//通過庫函數發送字符串//pStr ++;while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET);//判斷發送標志位,是否發送結束} }參考資料:
在main 中調用函數
UsartPrintf(USART3,"Distance:%dMode:%d",TCRT5000_Dist(),Mode);在手機APP顯示數據
OLED顯示
找資料
鏈接:
0.96寸(4管腳)資料下載鏈接: https://pan.baidu.com/s/1J57Izsv-PKmbwVrA2ynDzg 提取碼:vktz測試例程-現象正常-更改引腳-現象正常-移植到自己的工程
拷貝移植文件
一般移植傳感器的xxx.c 和xxx.h
復制相關文件到工程
KEIL中添加文件
添加頭文件路徑
對比程序移植
根據原理圖更改初始化函數
SCL–PC14
SDA–PC15
修改OLED_Init() 函數
//初始化SSD1306 void OLED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能A端口時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15|GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHzGPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC14,15GPIO_SetBits(GPIOC,GPIO_Pin_15|GPIO_Pin_14); delay_ms(800);OLED_WR_Byte(0xAE,OLED_CMD);//--display offOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_WR_Byte(0x40,OLED_CMD);//--set start line address OLED_WR_Byte(0xB0,OLED_CMD);//--set page addressOLED_WR_Byte(0x81,OLED_CMD); // contract controlOLED_WR_Byte(0xFF,OLED_CMD);//--128 OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverseOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 dutyOLED_WR_Byte(0xC8,OLED_CMD);//Com scan directionOLED_WR_Byte(0xD3,OLED_CMD);//-set display offsetOLED_WR_Byte(0x00,OLED_CMD);//OLED_WR_Byte(0xD5,OLED_CMD);//set osc divisionOLED_WR_Byte(0x80,OLED_CMD);//OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode offOLED_WR_Byte(0x05,OLED_CMD);//OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge PeriodOLED_WR_Byte(0xF1,OLED_CMD);//OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartionOLED_WR_Byte(0x12,OLED_CMD);//OLED_WR_Byte(0xDB,OLED_CMD);//set VcomhOLED_WR_Byte(0x30,OLED_CMD);//OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enableOLED_WR_Byte(0x14,OLED_CMD);//OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel }修改oled.h中的宏
//-----------------OLED IIC端口定義---------------- #define OLED_SCLK_Clr() GPIO_ResetBits(GPIOC,GPIO_Pin_14)//SCL #define OLED_SCLK_Set() GPIO_SetBits(GPIOC,GPIO_Pin_14)#define OLED_SDIN_Clr() GPIO_ResetBits(GPIOC,GPIO_Pin_15)//SDA #define OLED_SDIN_Set() GPIO_SetBits(GPIOC,GPIO_Pin_15)#define OLED_CMD 0 //寫命令#define OLED_DATA 1 //寫數據調用初始化函數
OLED_Init(); //初始化OLED OLED_Clear();在main使用顯示函數
顯示字符串
OLED_ShowString(6,3,"ABCDSDKJF",16);//顯示一個字符號串OLED_ShowString(0,6,"GFGFGF:",16); OLED_ShowString(63,6,"FGFGFG:",16);顯示數據的一種方法
u8 string[10] = {0}; //定義在前面 ...sprintf((char *)string,"Pitch:%.2f",pitch);OLED_ShowString(6,3,string,16);ADC測量電池電壓
ADC是嘛
百度百科介紹:
我們知道萬用表 電壓表可以測量電池,或者電路電壓。那么我們是否可以通過單片機獲得電壓,方便我們監控電池狀態
如何測量我們的鋰電池電壓那?鋰電池電壓12V左右,單片機ADC最大測量電壓3.3V,這里我們需要分壓電路分壓。
通過測量ADC點的電壓就可以計算VBAT_IN的電壓。
移植程序
拷貝文件
在adc.c的程序
#include "adc.h"#include "delay.h"//初始化ADC //這里我們僅以規則通道為例 //我們默認將開啟通道0~3 void Adc_Init(void) { ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道時鐘RCC_ADCCLKConfig(RCC_PCLK2_Div6); //設置ADC分頻因子6 72M/6=12,ADC最大時間不能超過14M//PA1 作為模擬通道輸入引腳 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模擬輸入引腳GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_DeInit(ADC1); //復位ADC1,將外設 ADC1 的全部寄存器重設為缺省值ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在獨立模式ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模數轉換工作在單通道模式ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模數轉換工作在單次轉換模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //轉換由軟件而不是外部觸發啟動ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC數據右對齊ADC_InitStructure.ADC_NbrOfChannel = 1; //順序進行規則轉換的ADC通道的數目ADC_Init(ADC1, &ADC_InitStructure); //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器 ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1ADC_ResetCalibration(ADC1); //使能復位校準 while(ADC_GetResetCalibrationStatus(ADC1)); //等待復位校準結束ADC_StartCalibration(ADC1); //開啟AD校準while(ADC_GetCalibrationStatus(ADC1)); //等待校準結束// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的軟件轉換啟動功能} //獲得ADC值 //ch:通道值 0~3 u16 Get_Adc(u8 ch) {//設置指定ADC的規則組通道,一個序列,采樣時間ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采樣時間為239.5周期 ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的軟件轉換啟動功能 while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待轉換結束return ADC_GetConversionValue(ADC1); //返回最近一次ADC1規則組的轉換結果 }u16 Get_Adc_Average(u8 ch,u8 times) {u32 temp_val=0;u8 t;for(t=0;t<times;t++){temp_val+=Get_Adc(ch);delay_ms(5);}return temp_val/times; }初始化函數
Adc_Init(); //ADC初始化
在main中測量并顯示
while(1){adcx=Get_Adc_Average(ADC_Channel_4,10);//LCD_ShowxNum(156,130,adcx,4,16,0);//顯示ADC的值temp=(float)adcx*(3.3/4096);adcx=temp;sprintf((char *)string,"temp:%.2f",temp);OLED_ShowString(6,3,string,16); }超聲波測距
通過超聲波的硬件介紹我們知道
MCU給Trig腳一個大于10us的高電平脈沖;然后讀取Echo腳的高電平信號時間,通過公式:距離 = T*聲速/2 就可以算出來距離。
軟件方面:10us高電平脈沖通過GPIO輸出實現,高電平信號時間我們通過定時器的輸入捕獲來計算的。
初始化脈沖引腳PA0
在led.c中的SR04初始化函數
void SR04_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_SetBits(GPIOA,GPIO_Pin_0); }led.h有關宏定義和聲明
#define SR04 PAout(0) // PA0 void SR04_GPIO_Init(void);初始化PA1輸入捕獲
查看數據手冊
初始化定時器2 通道2 輸入捕獲相關功能
//定時器2通道2輸入捕獲配置TIM_ICInitTypeDef TIM2_ICInitStructure;void TIM2_Cap_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PA1 清除之前設置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA1 輸入 GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_ResetBits(GPIOA,GPIO_Pin_1); //PA1 下拉//初始化定時器5 TIM5 TIM_TimeBaseStructure.TIM_Period = arr; //設定計數器自動重裝值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //預分頻器 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設置時鐘分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位//初始化TIM5輸入捕獲參數TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2; // 選擇輸入端 IC2映射到TI2上TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI2上TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻 TIM2_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置輸入濾波器 不濾波TIM_ICInit(TIM2, &TIM2_ICInitStructure);//中斷分組初始化NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2中斷NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占優先級2級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //從優先級0級NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的參數初始化外設NVIC寄存器 TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC2,ENABLE);//允許更新中斷 ,允許CC2IE捕獲中斷 TIM_Cmd(TIM2,ENABLE ); //使能定時器2} u8 TIM5CH1_CAPTURE_STA=0; //輸入捕獲狀態 u16 TIM5CH1_CAPTURE_VAL; //輸入捕獲值//定時器2中斷服務程序 void TIM2_IRQHandler(void) { if((TIM5CH1_CAPTURE_STA&0X80)==0)//還未成功捕獲 { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){ if(TIM5CH1_CAPTURE_STA&0X40)//已經捕獲到高電平了{if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高電平太長了{TIM5CH1_CAPTURE_STA|=0X80;//標記成功捕獲了一次TIM5CH1_CAPTURE_VAL=0XFFFF;}else TIM5CH1_CAPTURE_STA++;} }if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕獲2發生捕獲事件{ if(TIM5CH1_CAPTURE_STA&0X40) //捕獲到一個下降沿 { TIM5CH1_CAPTURE_STA|=0X80; //標記成功捕獲到一次上升沿TIM5CH1_CAPTURE_VAL=TIM_GetCapture2(TIM2);TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC2P=0 設置為上升沿捕獲}else //還未開始,第一次捕獲上升沿{TIM5CH1_CAPTURE_STA=0; //清空TIM5CH1_CAPTURE_VAL=0;TIM_SetCounter(TIM2,0);TIM5CH1_CAPTURE_STA|=0X40; //標記捕獲到了上升沿TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling); //CC2P=1 設置為下降沿捕獲} } }TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中斷標志位}在time 中聲明初始化函數
void TIM2_Cap_Init(u16 arr,u16 psc);計算輸出距離
在main.c聲明變量
extern u8 TIM5CH1_CAPTURE_STA; //輸入捕獲狀態 extern u16 TIM5CH1_CAPTURE_VAL; //輸入捕獲值定義變量
int Distance =0; int time=0;調用初始化函數
SR04_GPIO_Init(); TIM2_Cap_Init(0XFFFF,72-1); //以1Mhz的頻率計數完成測距的函數
delay_ms(500);//加入延時HC_SR04 =0;delay_us(10);HC_SR04 = 1; if(TIM5CH1_CAPTURE_STA&0X80)//成功捕獲到了一次上升沿{time=TIM5CH1_CAPTURE_STA&0X3F;time*=65536;//溢出時間總和time+=TIM5CH1_CAPTURE_VAL;//得到總的高電平時間printf("\r\nHIGH:%d us\r\n",time);//打印總的高點平時間Distance = time*0.033/2;printf("cm:%d\r\n",Distance);TIM5CH1_CAPTURE_STA=0;//開啟下一次捕獲}封裝一下方便調用
int TCRT5000_Dist(void) {HC_SR04 = 1;delay_us(13);HC_SR04=0;if(TIM5CH1_CAPTURE_STA&0X80)//成功捕獲到了一次上升沿{time=TIM5CH1_CAPTURE_STA&0X3F;time*=65536;//溢出時間總和time+=TIM5CH1_CAPTURE_VAL;//得到總的高電平時間printf("\r\nHIGH:%d us\r\n",time);//打印總的高點平時間Distance = time*0.033/2;printf("cm:%d\r\n",Distance);TIM5CH1_CAPTURE_STA=0;//開啟下一次捕獲}return Distance;}使用串口助手查看結果
練一練–編寫定距離跟隨功能
功能:根據根據超聲波測量距離跟隨前方物體
while(1){HC_SR04 = 1;delay_us(13);HC_SR04=0;if(TIM5CH1_CAPTURE_STA&0X80)//成功捕獲到了一次上升沿{time=TIM5CH1_CAPTURE_STA&0X3F;time*=65536;//溢出時間總和time+=TIM5CH1_CAPTURE_VAL;//得到總的高電平時間printf("\r\nHIGH:%d us\r\n",time);//打印總的高點平時間Distance = time*0.033/2;printf("cm:%d\r\n",Distance);TIM5CH1_CAPTURE_STA=0;//開啟下一次捕獲}if(Distance>20){Forward();delay_ms(50);}if(Distance<15){Backward();delay_ms(50);}AIN1 =0;AIN2 = 0;BIN1 = 0;BIN2 =0;}練一練–結合舵機完成避障功能
功能:通過舵機旋轉不同角度,超聲波測量左右是否存在障礙物,控制小車運動。
測試舵機的轉角,不同占空比小車舵機的角度
TIM_SetCompare1(TIM3,80); TIM_SetCompare1(TIM3,50); TIM_SetCompare1(TIM3,110);整體邏輯
if(Mode == 3){ //超聲波避障TIM_SetCompare1(TIM3,80); //舵機向前 使超聲波朝前方delay_ms(200);if(TCRT5000_Dist()>25)// 前方無障礙物{Forward();delay_ms(500);}if(TCRT5000_Dist()<25) //向前有障礙物{TIM_SetCompare1(TIM3,50); //舵機向右邊轉大約30度delay_ms(200);if(TCRT5000_Dist()>25)//右側無障礙物判斷{Rightward();delay_ms(700);}else { //右邊有障礙物TIM_SetCompare1(TIM3,100); //舵機向左邊轉大約30度delay_ms(200);if(TCRT5000_Dist()>25)//左側無障礙物{Leftward();delay_ms(700); }else{Backward();//后退delay_ms(700);Rightward(); //右轉delay_ms(700);} }} } }綜合一下-縫合上面練一練的功能
功能:
- 小車具有紅外對管循跡、藍牙遙控、定距離跟隨、避障運動模式
- 可以通過小車按鍵和APP進行切換小車的運動模式。
- APP與OLED顯示小車所處模式和超聲波測量值、電池電壓。
實現切換功能必須
main中的循環
while(1){ sprintf((char *)string,"Distance:%d ",TCRT5000_Dist());// 顯示距離信息 這里的 %d 需要幾個空格OLED_ShowString(6,3,string,16); sprintf((char *)string,"Mode:%d",Mode);//顯示小車模式OLED_ShowString(6,6,string,16); if(Mode == 1){//定距離跟隨TIM_SetCompare1(TIM3,80); //超聲波舵機向前if(TCRT5000_Dist()>25)// 距離太遠{Forward();delay_ms(200);}if(TCRT5000_Dist() <20)//距離太近{Backward();delay_ms(200); }AIN1 =0;//車輛暫定 如果不加 小車就會一直往前或者一直往后AIN2 =0;BIN1 =0;BIN2 =0; }if(Mode == 2){//藍牙控制小車TIM_SetCompare1(TIM3,80); //超聲波舵機向前if(g_USART3_FLAG1 == 1) //前進{g_USART3_FLAG1=0;Forward();delay_ms(500);}if(g_USART3_FLAG1 == 2) //向右{g_USART3_FLAG1=0;Rightward();delay_ms(500);}if(g_USART3_FLAG1 ==3) //向左{g_USART3_FLAG1=0;Leftward();delay_ms(500);}if(g_USART3_FLAG1 ==4) //向后{g_USART3_FLAG1=0;Backward();delay_ms(500);}if(g_USART3_FLAG1 ==5) //停止{g_USART3_FLAG1=0;AIN1=0;AIN2=0;BIN1=0;BIN2=0;delay_ms(500);}} if(Mode == 3){ //超聲波避障TIM_SetCompare1(TIM3,80); //舵機向前delay_ms(200);if(TCRT5000_Dist()>25)// 前方無障礙物{Forward();delay_ms(500);}if(TCRT5000_Dist()<25) //向前有障礙物{TIM_SetCompare1(TIM3,50); //舵機右轉delay_ms(200);if(TCRT5000_Dist()>25)//右側無障礙物判斷{Rightward();delay_ms(700);}else {TIM_SetCompare1(TIM3,100); //舵機向左 delay_ms(200);if(TCRT5000_Dist()>25)//左側無障礙物{Leftward();delay_ms(700); }else{Backward();delay_ms(700);//后退Rightward();delay_ms(700);} }} }if(Mode == 4){//紅外循跡TIM_SetCompare1(TIM3,80); //超聲波舵機向前if(HW_1 == 0&&HW_2 == 0&&HW_3 == 0&&HW_4 == 0)//應該前進 {Forward();delay_ms(20);}if(HW_1 == 0&&HW_2 == 1&&HW_3 == 0&&HW_4 == 0)//應該右邊{Rightward();delay_ms(150); }if(HW_1 == 1&&HW_2 == 0&&HW_3 == 0&&HW_4 == 0)//應該右邊{Rightward();delay_ms(270);}if(HW_1 == 1&&HW_2 == 1&&HW_3 == 0&&HW_4 == 0)//應該右邊{Rightward();delay_ms(370);}if(HW_1 == 0&&HW_2 == 0&&HW_3 == 1&&HW_4 == 0)//應該左邊{Leftward();delay_ms(150);}if(HW_1 == 0&&HW_2 == 0&&HW_3 == 0&&HW_4 == 1)//應該左邊{ Leftward();delay_ms(270); }if(HW_1 == 0&&HW_2 == 0&&HW_3 == 1&&HW_4 == 1)//應該左邊{Leftward();delay_ms(370); }} if(Mode ==0){delay_ms(1);AIN1=0;AIN2=0;BIN1=0;BIN2=0;}}總結
以上是生活随笔為你收集整理的stm32项目_stm32f103c8t6项目_循迹避障小车完整制作过程_智能小车设计_STM32智能小车教程-循迹-避障-蓝牙遥控-跟随的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 反射无参方法_无参构造方法 ?
- 下一篇: 关键路径 详解 (前置知识:拓扑排序)