日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

stm32f407igh6学习笔记

發(fā)布時間:2024/3/26 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stm32f407igh6学习笔记 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?本文將記錄我的學習歷程,基于rm機甲大師實驗室,主要包括各種函數(shù)的應用及相應硬件模塊的介紹。閱讀需要有51單片機和基本的寄存器基礎。

前言:

stm32cubemx時鐘樹配置

12 HSE /6 x168 /2 PLLCLK /4 /2

c語言基礎

一 定義結構體 struct Books {char title[50];char author[50];char subject[100];int book_id; } book;//此聲明聲明了擁有四個變量的結構體,同時聲明了結構體變量book,它的類型(標簽)是struct Booksstruct Books book2,book3[20],*book4;//用struct Books標簽又聲明了三個結構體變量//也可以用typedef創(chuàng)建新類型 typedef struct {char title[50];char author[50];char subject[100];int book_id; } Books2; //現(xiàn)在可以用Books2作為類型聲明新的結構體變量 Books2 book5,book6[20],*book7;結構體的成員可以包含其他結構體,也可以包含指向自己結構體類型的指針(在代碼中常見) //此結構體的聲明包含了其他的結構體 struct COMPLEX {char string[100];struct Books book; };//此結構體的聲明包含了指向自己類型的指針 struct NODE {char string[100];struct NODE *next_node; };二 結構體變量的初始化 #include <stdio.h>struct Books {char title[50];char author[50];char subject[100];int book_id; } book = {"C 語言", "RUNOOB", "編程語言", 123456};int main() {printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id); }三 訪問結構成員 非指針用“.”,指針用“->” 如struct Books {char title[50];char author[50];char subject[100];int book_id; } book book.title book.author ...... 如struct Books {char title[50];char author[50];char subject[100];int book_id; } *book2 book2->title book2->author ....... book2=&book

一 點亮LED

1 原理:三個 LED 燈的引腳為 PH10,PH11,PH12。在user label 填寫命名LED_B LED_G LED_R 對應引腳輸出高電平點亮。

2 代碼:

點亮LED HAL_GPIO_WritePin(LED_R_GPIO_Port, LED_R_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED_G_GPIO_Port, LED_G_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_SET); 或 HAL_GPIO_WritePin(GPIOH, GPIO_PIN_12, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOH, GPIO_PIN_11, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOH, GPIO_PIN_10, GPIO_PIN_SET); LED閃爍 HAL_GPIO_TogglePin(GPIOH,GPIO_PIN_10); HAL_Delay(500); HAL_GPIO_TogglePin(GPIOH,GPIO_PIN_11); HAL_Delay(500); HAL_GPIO_TogglePin(GPIOH,GPIO_PIN_12); HAL_Delay(500); 參數(shù):端口、引腳 功能:對應端口的引腳電平翻轉(zhuǎn) 藍色-青色(藍色和綠色混合)-白色(紅藍綠混合)-重灰色(紅綠混合)-紅色-滅 GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) //讀取對應引腳的電平并返回

二 flash LED

三 定時器閃爍LED

1 原理:

定時器:分配+計數(shù)+重載

(1)預分頻寄存器TIMx_PSC:時鐘源的信號經(jīng)過TIMx_PSC按其中的分頻比Prescaler進行分頻(頻率除以Prescaler)

(2)自動重裝載寄存器 TIMx_ARR :計數(shù)器寄存器TIMx_CNT 根據(jù)時鐘的頻率向上計數(shù),直到 等于TIMx_ARR的自動重裝載值Counter Period,產(chǎn)生一個定時中斷觸發(fā)信號,TIMx_CNT 被清空, 并重新從 0 開始向上計數(shù)。

定時器周期=[(分頻值+1)(重載值+1)]/時鐘源頻率

中斷:當多個中斷發(fā)生時,先根據(jù)搶占優(yōu)先級判斷哪個中斷分組能夠優(yōu)先響應再到這個中斷分組 中根據(jù)各個中斷的響應優(yōu)先級判斷哪個中斷優(yōu)先響應

it.c文件中有我們配置好使能的中斷類型,找到這個中斷服務函數(shù)。 里面有中斷回調(diào)函數(shù),我們在主文件中對此進行重新編寫。1 HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)? 參數(shù):*htim 定時器的句柄指針,如定時器 1 就輸 入&htim1,定時器 2 就輸入&htim2 作用:中斷服務函數(shù)2 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)? 參數(shù):&htim1... 作用:中斷回調(diào)函數(shù)。 我們可以在別處重寫中斷回調(diào)函數(shù),一般我們需要在中斷回調(diào)函數(shù)中判斷中斷來源并執(zhí)行相應的用戶操作。 (如定時器1啊等等 if(htim == &htim1))3?HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim) 參數(shù):略 HAL_StatusTypeDef是HAL 庫定義的幾種狀態(tài), 如果成功使定時器開始工作,則返回 HAL_OK 作用:使定時器開始工作4?HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim) 參數(shù):略 作用:使對應的定時器開始工作,并使能其定時中斷定時器配置-使能定時器中斷-定時器計數(shù)值滿進入中斷-中斷內(nèi)部先進入中斷服務函數(shù)再進入中斷回調(diào)函數(shù)

2 代碼

main.c文件中: 主函數(shù): 各種初始化(包含定時器的初始化)//此部分內(nèi)容配置好了,不用自己寫 HAL_TIM_Base_Start_IT(&htim1);//開啟定時器1的中斷模式,開始工作中斷回調(diào)函數(shù): void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {if(htim == &htim1){//500ms triggerbsp_led_toggle();} }/*主函數(shù)開啟定時器1以后,每到達時間就會進入中斷服務函數(shù)中的中斷回調(diào)函數(shù)。 中斷回調(diào)函數(shù)在hal庫的別的文件下有虛定義__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim),我們需要在主文件下重寫它。 進入主文件下的中斷回調(diào)函數(shù)以后,首先判斷是不是定時器1,再實現(xiàn)LED燈的翻轉(zhuǎn)。*/

四. PWM 控制 LED 的亮度

1 原理:

脈沖調(diào)制有兩個重要的參數(shù):

1 輸出頻率,頻率越高,數(shù)字信號代替模擬信號的效果越好。

2 占空比。占空比就是改變輸出模擬效果的電壓大小。占空比越大則模擬出的電壓越大。

算法原理:

1 顏色:16進制對應4位。32位的aRGB變量每8位分別代表了 alpha(透明度)Red(紅色)Green(綠色)和 Blue(藍色)四個要素。

如純紅色表示為 0xFFFF0000(透明度位置FF,紅色位置FF,其他位置0),純綠色表示為 0xFF00FF00,純藍色表示為 0xFF0000FF。黃色由藍色和綠色合成,所以可以表示為 0xFF00FFFF。

通過移位操作獲得每個顏色表示的八位變量,如red = ((RGB_flow_color[i] & 0x00FF0000) >> 16)。然后將這個值作為定時器通道的比較值控制LED燈的亮度調(diào)節(jié)。

//以上這段非人話,個人認為例程的這段代碼寫復雜了,沒必要。

2 定時器:定時器設置為PWM模式。配置定時器中的比較寄存器TIMx_CCRx,計數(shù)寄存器的值不斷增加,小于比較值的時候PWM輸出高電平,大于比較值的時候PWM輸出低電平。

占空比 𝑃 =( 𝑇𝐼𝑀_𝐶𝐶𝑅𝑥 ? 1 )/𝑇𝐼𝑀_𝐴𝑅𝑅 ? 100%。

3 定時器的pwm模式:

每一個定時器的pwm模式有4個通道,每一個通道都有對應標號的比較寄存器,比如定時器5的 1 號通道對應的比較寄存器為 TIM5_CCR1。

可以注意到5號定時器三個通道對應的引腳正是之前的實驗中使用的 LED 引腳,所以如果對5號定時器的通道的比較值進行配置,就能夠控制每一個LED引腳的電壓大小,從而改變燈的亮度。

__HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_1, blue); 參數(shù):哪個定時器,哪個通道,比較的值。blue為變量,可變。 作用:通過這個函數(shù)將比較值賦值給對應定時器通道的比較寄存器? ? 比較寄存器的值一直在變,輸出電壓一直在變,燈亮度改變。

2 代碼:

main.c文件中: 主函數(shù): //開啟定時器 HAL_TIM_Base_Start(&htim5); //開啟PWM通道,分別對應三個LED燈的引腳。LED和定時器硬件都連在這一個引腳上,定時器控制的電壓變化那么燈亮度就變化。 HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_3); 其他略 __HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_1, blue); __HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_2, green); __HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_3, red);

五. 蜂鳴器&舵機

1 原理:

蜂鳴器: 輸入方波頻率的不同產(chǎn)生不同的音調(diào)。蜂鳴器使用的引腳為 PD14,為定時器 4 的通道 3。Pulse可以設置比較寄存器的初始值。

時鐘的頻率是72MHZ,可以理解為一秒鐘STM32會自己數(shù)72M次;分頻系數(shù)是72,則該時鐘的頻率會變成72MHZ/72=1MHZ,一秒鐘STM32會自己數(shù)1M次,但是在設置的時候要注意,數(shù)值應該是72-1;需要定時1ms,由于1ms=1us*1000,那么預裝載值就是1000-1。在波形圖上分頻系數(shù)決定了多久來一個點(太快了所以連點成線),重載值決定了波形周期,比較值決定了高電平的時間。

__HAL_TIM_PRESCALER(&htim4, psc)//設置定時器4的分屏系數(shù),HAL庫中重載值也可以設置 __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwm);//設置定時器4的通道3的比較值 //psc和pwm都是一個變化的值,所以蜂鳴器連接引腳的電壓一直在變化,發(fā)出音調(diào)變化

舵機:舵機的引腳為定時器1的通道1、2、3、4和定時器8的通道1、2、3。

時鐘源/(分頻+1)/(重載值+1)=50

舵機使用的 PWM 信號一般為頻率 50Hz,舵機的控制一般需要一個20ms的時基脈沖,該脈沖的高電平部分一般為0.5ms~2.5ms范圍內(nèi)的角度控制脈沖部分,以180度電機為例:0.5ms————–0度;1.0ms————45度;1.5ms————90度;2.0ms———–135度;2.5ms———–180度。

MG995舵機,根據(jù)輸入pwm占空比的不同調(diào)節(jié)轉(zhuǎn)動的角度。如果我們想要用遙控器操縱舵機,那么首先遙控器初始化(包含DMA初始化),對遙控器遙感變動時候相應變動的寄存器進行編寫(當遙控器寄存器大于小于xx值的時候,pwm咋樣咋樣),實現(xiàn)控制舵機的效果。具體見例程PWM_SNALL。

2 代碼:設置占空比就行,略。

六. 按鍵的外部中斷

1 原理:

外部中斷通常是 GPIO 的電平跳變引起的中斷。在 stm32 中,每一個 GPIO 都可以作為外 部中斷的觸發(fā)源,外部中斷一共有 16 條線,對應著 GPIO 的 0-15 引腳,每一條外部中斷 都可以與任意一組的對應引腳相連。上升沿中斷、下降沿中斷、上升沿和下降沿中斷。

配置:將相應引腳設置為外部中斷模式,然后記得使能外部中斷。

外部中斷回調(diào)函數(shù)HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin),在主文件中重寫,首先判斷是哪個引腳產(chǎn)生的外部中斷看看對不對。每當產(chǎn)生一次外部中斷就進入中斷服務函數(shù)先對中斷寄存器進行處理然后進入中斷回調(diào)函數(shù)。

注意消抖。

2 代碼:

過幾天自己寫一個

七 ADC采樣

1 原理:

(1)概念:分辨率=量程/2的n次方,代表一格數(shù)字量所反映的電壓等模擬輸入量。

(2)過程:先采樣,再保持,再量化(變成能編碼的量。如輸入電壓量程為4v,位數(shù)為3位,那么分辨率是0.5,若采樣到的信號大小是3.6v無法編碼,所以需要量化為3.5v,這樣才能表示為111),最后編碼成數(shù)字量給單片機。

(3)怎么測我們需要的地方的電壓:ADC3的通道8連接電源,可以測電源的輸出電壓;其他通道連接其他地方,可以測其他內(nèi)部元件的輸出電壓。通過ADC,可以將電機、各種傳感器等的模擬量轉(zhuǎn)換為單片機處理的數(shù)字量。

(4)程序思路:從程序角度阻塞式先開啟ADC,然后判斷是否轉(zhuǎn)化完成,最后讀取通道中ADC的值;非阻塞式的話開啟ADC,然后去中斷函數(shù)里面讀取ADC的值。

非中斷: HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc)?開啟ADC的采樣,如果是adc1就輸入&hadc1,adc2就輸入&adc2,adc3就輸入&hadc3 HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout)?等待 ADC轉(zhuǎn)換結束 uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc)?獲取ADC值中斷: HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc) 開啟ADC的采樣 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) ADC的中斷回調(diào)函數(shù),我們在此之中寫代碼

2 代碼:

過幾天自己寫一個

八 串口接收與發(fā)送

1 原理:

串口通訊,收發(fā)雙方要遵從同樣的協(xié)議才能完成數(shù)據(jù)傳輸,同時波特率也要相等,波特率可以設置為115200,38400,9600 等。

//使能接收中斷和空閑中斷 __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //receive interrupt __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //idle interrupt __HAL_UART_ENABLE_IT(&huart6, UART_IT_RXNE); //receive interrupt __HAL_UART_ENABLE_IT(&huart6, UART_IT_IDLE); //idle interrupt發(fā)送函數(shù) HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); //如串口1就輸入&huart1;發(fā)送的數(shù)據(jù)的首地址,比如要發(fā)送buf[]=”Helloword”則輸入buf;要發(fā)送的數(shù)據(jù)大小,可以用sizeof;等待的時間接收函數(shù) HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);發(fā)送中斷函數(shù) HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); 接收中斷函數(shù) HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

串口接收中斷即每當串口完成一次接收之后觸發(fā)一次中斷;串口空閑中斷即每當串口接收完一幀數(shù)據(jù)后又過了一個字節(jié)的時間沒有接收到任何數(shù)據(jù)則觸發(fā)一次中斷。

當串口發(fā)生接收中斷或者空閑中斷時,都會進入 USARTx_IRQHandler 中斷處理函數(shù)。在中 斷處理函數(shù)中通過串口的狀態(tài)寄存器來判斷產(chǎn)生中斷的是接收中斷還是空閑中斷,然后進入相應的回調(diào)函數(shù)處理。

即在HAL中有中斷服務函數(shù)void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);

這個庫函數(shù)幫我們完成了中斷類型判斷和清除標志位,我們只需要在具體的函數(shù)中寫邏輯即可。上面這個庫函數(shù)判斷出不同的類型,然后調(diào)用不同的回調(diào)函數(shù),我們處理接收中斷回調(diào)函數(shù)HAL_UART_TxCpltCallback即可。?

回調(diào)函數(shù)如下: 1 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); 2 void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); 3 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); 4 void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart); 5 void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart); 6 void HAL_UART_AbortCpltCallback(UART_HandleTypeDef *huart); 7 void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart); 8 void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart);

2 代碼:

九 遙控器&串口的DMA模式

1 原理:

DMA:DMA 是在使用串口進行通訊時常用的一個功能,使用該功能能夠完成串口和內(nèi)存之間直接的數(shù)據(jù)傳送,而不需要 CPU 進行控制,從而節(jié)約 CPU 的處理時間。

緩沖區(qū)的作用是:數(shù)據(jù)通過DMA串口傳進來以后我們要先在緩沖區(qū)中保存,然后進行一系列操作處理判斷檢驗接收到的數(shù)據(jù)是否為我們需要的那個數(shù)據(jù)(判斷對不對,通過比較位數(shù)是否相等的方式),若正確再放在內(nèi)存中。緩沖區(qū)是DMA模式下內(nèi)存中的一片區(qū)域,我們理解為數(shù)據(jù)進入內(nèi)存存儲前的一片區(qū)域即可。

注意:串口DMA的初始化代碼是要自己寫的,可以從例程中復制。

代碼思路:RC_init(內(nèi)含USART3的DMA的初始化:使能DMA串口接收-使能空閑中斷(空閑中斷要信號觸發(fā)后過一個字節(jié)的時間才能空閑中斷標志位置1,所以時間足夠底下我們的初始化程序全部完成以后才會進中斷服務函數(shù)對應的空閑中斷觸發(fā)部分)-失效DMA-配置緩沖區(qū)(緩沖區(qū)的位置在哪,能容納的數(shù)據(jù)長度多少)-使能緩沖區(qū)-使能DMA)

遙控器寄存器的值是從-660到660(在代碼里已經(jīng)減去相應的量了)。


右遙控器前后是通道1,前正后負;左右是通道0,左負右正
左遙控器左右是通道3,前正后負;左遙控器左右是通道2,左負右正

左邊開關上中下對應s1 1,3,2
右邊開關上中下對應s0 1,3,2

HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff)); //串口發(fā)送Senbuff數(shù)組 HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); //串口通過DMA接受指定長度的數(shù)據(jù)

?串口1的DMA用于發(fā)送數(shù)據(jù),即單片機向電腦發(fā)送數(shù)據(jù)(顯示一些信息);串口3的DMA用于接收數(shù)據(jù),即遙控器外設發(fā)送數(shù)據(jù)單片機的存儲區(qū)域接收。

??2 代碼:?

const RC_ctrl_t *local_rc_ctrl;//local_rc_ctrl是RC_ctrl_t變量類型的指針結構體變量名 remote_control_init();//紅外遙控的初始化,內(nèi)含接收串口3的DMA初始化 usart1_tx_dma_init();//發(fā)送串口1的DMA初始化 //以上這些初始化代碼都要自己寫,紅外遙控初始化拷貝相應文件,發(fā)送串口1初始化見后。 local_rc_ctrl = get_remote_control_point();//獲取紅外遙控器的指針,它就可以指向紅外遙控器的各種寄存器。 //local_rc_ctrl->rc.ch[0], local_rc_ctrl->rc.ch[1], local_rc_ctrl->rc.ch[2]等//發(fā)送串口1初始化 void usart1_tx_dma_init(void) {//enable the DMA transfer for the receiver request//使能DMA串口接收SET_BIT(huart1.Instance->CR3, USART_CR3_DMAT); } void usart1_tx_dma_enable(uint8_t *data, uint16_t len) {//disable DMA//失效DMA__HAL_DMA_DISABLE(&hdma_usart1_tx);while(hdma_usart1_tx.Instance->CR & DMA_SxCR_EN){__HAL_DMA_DISABLE(&hdma_usart1_tx);}//clear flag//清除標志位__HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx, DMA_HISR_TCIF7);__HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx, DMA_HISR_HTIF7);//set data address//設置數(shù)據(jù)地址hdma_usart1_tx.Instance->M0AR = (uint32_t)(data);//set data length//設置數(shù)據(jù)長度hdma_usart1_tx.Instance->NDTR = len;//enable DMA//使能DMA__HAL_DMA_ENABLE(&hdma_usart1_tx); }

關于通信協(xié)議:

主從通信:主機輪(流)詢(問),從機應答。發(fā)送指令給從機,讓它執(zhí)行什么操作;向從機要數(shù)據(jù)。

每一次通信都必須由主機發(fā)起,從機不可以主動向主機發(fā)送數(shù)據(jù)。系統(tǒng)上電以后所有的設備都處于接收狀態(tài),所以應當先將主機調(diào)成發(fā)送模式,發(fā)送數(shù)據(jù)包;?主機轉(zhuǎn)成接收模式,接收從機發(fā)送的應答;

地址碼就是從機的地址,我們需要和哪一臺設備建立聯(lián)系(即ID)

功能碼就是我們需要對從機執(zhí)行什么操作(讀/寫,單個位/多個寄存器等等)

數(shù)據(jù)碼就是我們往寄存器/位里面塞這樣一個數(shù),它本身就代表了某種信息(控制指令)

?

?

?

十 flash讀寫

個人計算機都均有內(nèi)存和硬盤(外存),開發(fā)板芯片 stm32 同樣具有內(nèi)存 192Kbytes 的 SRAM 和 1Mbytes 的外存 flash。flash是閃存外設,可以把數(shù)據(jù)寫進去存儲,也可以從里面讀取數(shù)據(jù)。

flash的優(yōu)點是容量較大,掉電不會丟失數(shù)據(jù);缺點是寫入需要先擦除,讀取寫入速度較慢。

注意:1. 為了保護數(shù)據(jù)的安全性,flash 有專門的鎖寄存器,每次要對 flash 頁面進行修改時首先 要通過鎖寄存器對頁面進行解鎖,修改完成后要進行加鎖。2. flash 是不支持在保存原有數(shù)據(jù)的情況下進行修改的,因此要改變 flash 頁面數(shù)據(jù)時,需 要對這個頁面進行擦除,擦除之后再寫入新的數(shù)據(jù)。

HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError //flash擦除函數(shù),擦除指定的flash頁面。 //參數(shù):擦除 flash 時使用的結構體指針,需要創(chuàng)建一個 FLASH_EraseInitTypeDef 類型的結構體 flash_erase, 需要賦予這個結構體以下參數(shù):Sector(要擦除的頁面的首地址),TypeErase(擦除方式),VoltageRange(電壓范圍),NbSectors(待擦除頁面數(shù)),最后我們將&flash_erase 作為參數(shù)輸入函數(shù); 如果本次 flash 擦除產(chǎn)生了錯誤,則發(fā)生擦除錯誤的頁面號存儲在 SectorError 中HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data) //flash寫入函數(shù),以指定的方式,向 flash 中的一頁寫入數(shù)據(jù) 參數(shù):uint32_t TypeProgram 選擇寫入的數(shù)據(jù)格式,可以選擇8位字節(jié)FLASH_TYPEPROGRAM_BYTE , 16 位半字FLASH_TYPEPROGRAM_HALFWORD , 32 位 字FLASH_TYPEPROGRAM_WORD ,或者 64 位雙字FLASH_TYPEPROGRAM_DOUBLEWORD 需要寫入數(shù)據(jù)的地址;需要寫入的數(shù)據(jù)void flash_read(uint32_t address, uint32_t *buf, uint32_t len) {memcpy(buf, (void*)address, len *4); } //從 flash 讀取數(shù)據(jù) //參數(shù):Flash 地址;讀取后的存儲變量地址;字節(jié)長度 #define USER_FLASH_ADDRESS ADDR_FLASH_SECTOR_11 #define FLASH_DATA_LENGHT 12 //ADDR_FLASH_SECTOR_11即0x080E0000,是12個flash葉分區(qū)的第一個 uint8_t after_erase_data[FLASH_DATA_LENGHT]; uint8_t write_data[FLASH_DATA_LENGHT] = "RoboMaster\r\n"; uint8_t after_write_data[FLASH_DATA_LENGHT];//擦除flash頁 flash_erase_address(USER_FLASH_ADDRESS, 1); //read data from flash, before writing data //在寫數(shù)據(jù)之前,從flash讀取數(shù)據(jù) flash_read(USER_FLASH_ADDRESS, (uint32_t *)after_erase_data, (FLASH_DATA_LENGHT + 3) / 4); //write data to flash //往flash寫數(shù)據(jù) flash_write_single_address(USER_FLASH_ADDRESS, (uint32_t *)write_data, (FLASH_DATA_LENGHT + 3) / 4); //read data from flash, after writing data //在寫數(shù)據(jù)之后,從flash讀取數(shù)據(jù) flash_read(USER_FLASH_ADDRESS, (uint32_t *)after_write_data, (FLASH_DATA_LENGHT + 3) / 4);

十一 I2C?

串行同步通訊總線協(xié)議--I2C,使用 I2C可以配置和讀取 IST8310 磁力計的數(shù)據(jù),還可以用于溫度傳感器,氣壓傳感器,多路 ADC 模塊等多種傳感器。

IST8310 磁力計:測量地球磁場強度,用于計算機器人的朝向。

SCL、SDA分別為I2C的時鐘線和數(shù)據(jù)線,RSTN為IST8310 的 RESET,低電平重啟 IST8310,DRDY為IST8310 的數(shù)據(jù)準備(data ready)。

程序開始先進行 HAL 庫自帶的初始化,包括時鐘,GPIO,I2C3 的初始化;之后完成配置 IST8310,IST8310 的 DRDY 引腳會產(chǎn)生 200Hz 的周期信號;當 DRDY 下降沿,會引起單 片機的下降沿外部中斷;在外部中斷回調(diào)函數(shù)中,調(diào)用 ist8310 的讀取函數(shù),便可以讀取磁 場數(shù)據(jù)。

I2C通信協(xié)議

I2C 有兩根信號線,一根數(shù)據(jù)線 SDA,另一根是時鐘線 SCL。I2C 總線允許掛載多個主設備,但總線時鐘同一時刻只能由一個主設備產(chǎn)生,并且要求每個 連接到總線上的器件都有唯一的 I2C 地址,從設備可以被主設備尋址。

整個過程便是如同到學校的快遞柜(從機 I2C 地址),對第幾號柜箱(寄存器地址), 進行寄出或者簽收快遞(數(shù)據(jù))的過程。具體時序略。?

HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout) //從 I2C 設備的寄存器讀取數(shù)據(jù) //參數(shù): I2C句柄;I2C從機地址;寄存器地址;寄存器地址增加大小-I2C_MEMADD_SIZE_8BIT:增加八位,I2C_MEMADD_SIZE_16BIT:增加十六位;數(shù)據(jù)指針;數(shù)據(jù)長度;超時時間HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout) //往 I2C 設備的寄存器寫入數(shù)據(jù)

?十二 OLED

發(fā)送 I2C 地址后,發(fā)送數(shù)據(jù)類型再發(fā)送數(shù)據(jù),其中 數(shù)據(jù)類型為 0x00 表示控制指令,0x40 表示數(shù)據(jù)指令。

HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) //向某個 I2C 設備傳輸數(shù)據(jù) //參數(shù):I2C 句柄;I2C 從機地址;數(shù)據(jù)指針;數(shù)據(jù)長度;超時時間根據(jù) OLED 的通信方式,發(fā)送從機地址后需要在第一個字節(jié)中指明之后的數(shù)據(jù)類型,如果是控制指令 則需要發(fā)送 0x00,如果是數(shù)據(jù)指令則需要發(fā)送 0x40。函數(shù)原型如下:/*** @brief 寫數(shù)據(jù)或者指令到 OLED, 如果使用的是 SPI,請重寫這個函數(shù)* @param[in] dat: 要寫入的字節(jié)* @param[in] cmd: OLED_CMD 代表寫入的字節(jié)是指令; OLED_DATA 代表寫入的字節(jié)是數(shù)據(jù)* @retval none*/ oid oled_write_byte(uint8_t dat, uint8_t cmd) {static uint8_t cmd_data[2];if(cmd == OLED_CMD){cmd_data[0] = 0x00;}else{cmd_data[0] = 0x40;}cmd_data[1] = dat;HAL_I2C_Master_Transmit(&hi2c2, OLED_I2C_ADDRESS, cmd_data, 2, 10); }OLED_init 函數(shù),該函數(shù)主要配置 OLED 參數(shù),通過調(diào)用 oled_write_byte 傳入 OLED_CMD,傳輸控制指令完成配置OLED_display_on和OLED_display_off 函數(shù)分別是用來關閉OLED 顯示和開啟OLED 顯示OLED_draw_point 對(x,y)坐標的一個像素點進操作 OLED_draw_line 從(x1,y1)到(x2,y2)的直線經(jīng)過的像素點進行操作 OLED_show_char 顯示一個字符 OLED_show_string 顯示一個字符串 OLED_printf Printf 函數(shù)功能 OLED_LOGO 顯示 Robomaster LOGOOLED_operate_gram(pen_typedef pen) 函數(shù)是操作 OLED_GRAM[128][8]數(shù)組的,我們對整個數(shù)組進行 操作,操作完成后再通過 OLED_refresh_gram 函數(shù)整體刷新 OLED 內(nèi)部的 GRAM。作用是打開OLED顯示. 參數(shù):PEN_WRITE 是將數(shù)組都設置為 0xff,對于 OLED 屏幕即為全亮;PEN_CLEAR 是將數(shù)組都設置為 0x00,對于 OLED 屏幕即為全滅;PEN_INVERSION 是將數(shù)組的值全部反轉(zhuǎn),通過與 0xff 相減來實現(xiàn)。OLED_refresh_gram 函數(shù)功能是將內(nèi)部的GRAM[8][128]傳輸?shù)絆LED模塊的GRAM, 這樣 OLED 就會顯示圖像。

1. OLED 模塊的初始化,調(diào)用 OLED_init

2. 通過畫圖函數(shù),對 stm32 內(nèi)的 GRAM 數(shù)組進行操作

3. 調(diào)用 OLED_refresh_gram 函數(shù)將 GRAM 數(shù)據(jù)傳輸?shù)?OLED 模塊的 GRAM 進行顯示。

其中2、3步都是OLED_LOGO 函數(shù)完成的,內(nèi)集成畫圖函數(shù)將 GRAM 刷新成 LOGO 的數(shù)據(jù),以及最后調(diào)用了 OLED_refresh_gram 函數(shù)顯示。

十三 SPI?BMI088 傳感器

BMI088 是一種高性能慣性測量單元 (IMU),集成了 16 位 ADC 精度的三軸?螺儀和三軸加速度計。

陀螺儀能測量在三個正交方向上旋轉(zhuǎn)的角速度,也可以用于估算在三個方向上的旋轉(zhuǎn)角度,原理是將旋轉(zhuǎn)的角速度轉(zhuǎn)化為電容的變化即轉(zhuǎn)變?yōu)殡娦盘枴?/p>

加速度計能夠測量三個正交方向上的加速度,原理是加速度改變力改變電容改變轉(zhuǎn)化為電信號。

?我們實際上是對BMI088中的各種寄存器進行操作。

oid BMI088_read(fp32 gyro[3], fp32 accel[3], fp32 *temperate) BMI088_read(gyro, accel, &temp) //讀取角速度、加速度、溫度值

SPI通信協(xié)議:

SPI 的通信過程如下:

1. 主設備將要進行通訊的從設備的 SS/CS 片選拉低,

2. 主設備通過 SCK 向從設備提供同步通訊所需要的時鐘信號?

3. 主設備通過 MOSI 向從設備發(fā)送 8 位數(shù)據(jù),同時通過 MISO 接收從設備發(fā)來的 8 位數(shù) 據(jù)。

4. 通信結束,主設備拉高 SS/CS 片選。

HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout) //通過 SPI 進行主機和從機的通信 //參數(shù)1:SPI_HandleTypeDef *hspi 即 SPI 的句柄指針,如果是 SPI1 就輸入&hspi1,SPI2 就輸入&hspi2 參數(shù) 2 uint8_t *pTxData 待發(fā)送數(shù)據(jù)的首地址指針 參數(shù) 3 uint8_t *pRxData 接收數(shù)據(jù)的區(qū)域的首地址 參數(shù) 4 uint16_t Size 待發(fā)送的數(shù)據(jù)長度 參數(shù) 5 uint32_t Timeout 最大發(fā)送時長

十四?can總線

電機?

can發(fā)送:

?具體在CAN_cmd_chassis 函 數(shù) 和 CAN_cmd_gimbal 函數(shù)中,先將ID、數(shù)據(jù)位等信息賦值到我們定義的結構體變量中,再在內(nèi)部調(diào)用基于HAL 庫的CAN 發(fā)送函數(shù) HAL_CAN_AddTXMessag將這些結構體變量的值發(fā)送到內(nèi)部寄存器,就獲取了主機要發(fā)送給電機的信息。時序啊這些都是HAL庫內(nèi)部的東西,已經(jīng)封裝好了。

CAN_cmd_chassis(int16_t motor1, int16_t motor2, int16_t motor3, int16_t motor4) //CAN_cmd_chassis 函數(shù)的輸入為電機 1 到電機 4 的驅(qū)動電流期望值 motor1 到 motor4, 函數(shù)會將期望值拆分成高八位和第八位,放入 8Byte 的 CAN 的數(shù)據(jù)域中,然后添加 ID (CAN_CHASSIS_ALL_ID 0x200),幀格式,數(shù)據(jù)長度等信息,形成一個完整的 CAN 數(shù)據(jù)幀,發(fā)送給各個電調(diào)。CAN_cmd_gimbal(int16_t yaw, int16_t pitch, int16_t shoot, int16_t rev) //CAN_cmd_gimbal 函數(shù)的的功能為向云臺電機和發(fā)射機構電機發(fā)送控制信號, 輸入?yún)?shù)為 yaw 軸電機,pitch 軸電機,發(fā)射機構電機的驅(qū)動電流期望值 yaw,pitch,shoot(rev 為保留值), 函數(shù)會將期望值拆分成高八位和第八位,放入 8Byte 的 CAN 的數(shù)據(jù)域中,然后添 加 ID(CAN_GIMBAL_ALL_ID 0x1FF),幀格式,數(shù)據(jù)長度等信息,形成一個完整的 CAN 數(shù)據(jù)幀,發(fā)送給各個電調(diào)。//HAL 庫提供了實現(xiàn) CAN 發(fā)送的函數(shù) HAL_CAN_AddTXMessag。CAN_cmd_chassis 函數(shù)和AN_cmd_gimbal 函數(shù)內(nèi)部封裝了發(fā)送函數(shù)。 HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox) //將一段數(shù)據(jù)通過 CAN 總線發(fā)送 參數(shù):&hcan1;待發(fā)送的 CAN 數(shù)據(jù)幀信息的結構體指針,包含了 CAN 的 ID,格式等重要信息; 裝載了待發(fā)送的數(shù)據(jù)的數(shù)組名稱;用于存儲 CAN 發(fā)送所使用的郵箱號

?can接收:

?CAN 的接收中斷函數(shù)HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)每當 CAN 完成一幀數(shù)據(jù)的接收時,就會觸發(fā)一次 CAN 接收中斷處理函數(shù),接收中斷函數(shù)完成一些寄存器的處理之后會調(diào)用 CAN 接收中斷回調(diào)函數(shù)。在中斷回調(diào)函數(shù)中首先判斷接收對象的 ID,是否是需要的接收的電調(diào)發(fā)來的數(shù)據(jù)。完成判斷之后,進行解碼,將對應的電機的數(shù)據(jù)裝入電機信息數(shù)組 motor_chassiss各個對應的位中。接收中斷函數(shù)里接收時調(diào)用了 HAL 庫提供的接收函數(shù) HAL_CAN_GetRxMessage。

HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]) 接收 CAN 總線上發(fā)送來的數(shù)據(jù) 參數(shù):&hcan1;接收時使用的 CAN 接收 FIFO 號,一般為 CAN_RX_FIFO0; 存儲接收到的 CAN 數(shù)據(jù)幀信息的結構體指針,包含了 CAN 的 ID,格式等重要信息; 存儲接收到的數(shù)據(jù)的數(shù)組名稱

?can總線協(xié)議:主機發(fā)送信息給can總線上掛的設備(從機),每一個從機有一個對應的ID(地址),每當一個設備發(fā)送一幀數(shù)據(jù)時,總線其他設備會檢查這 個 ID 是否是自己需要接收數(shù)據(jù)的對象,如果是則接收本幀數(shù)據(jù),如果不是則忽略。若判斷可以接收以后,則發(fā)送控制位,規(guī)定了本幀數(shù)據(jù)的長度;再數(shù)據(jù)位,即 8 個 8 位數(shù)據(jù),CAN總線的一個數(shù)據(jù)幀中所要傳輸?shù)挠行?shù) 據(jù)實際上就是這 8Byte......

?數(shù)據(jù)位:如果要發(fā)送數(shù)據(jù)給 1 號到 4 號電調(diào),控制電機的輸出電流,從而控制電機轉(zhuǎn)速時,則置標識符(ID)為0x200,則8位數(shù)據(jù)位分別是4個電調(diào)的電流值高八位低八位(4x(1+1)),幀格式和 DLC 也按手冊規(guī)定置,完成數(shù)據(jù)發(fā)送。

而當要接收電調(diào)發(fā)送來的數(shù)據(jù)時,首先根據(jù)接收到的 ID 判斷究竟接收到的是哪個電調(diào)發(fā)送來的數(shù)據(jù)(0x200+電調(diào)ID,如電調(diào)1則為0x201),再對電調(diào)發(fā)送來的數(shù)據(jù)進行解碼(數(shù)據(jù)位的8位分別代表:轉(zhuǎn)子機械角度高低八位,轉(zhuǎn)子轉(zhuǎn)速高低八位,實際轉(zhuǎn)矩電流高低八位,電機溫度,null)獲取相關信息。

變量聲明應寫在.c文件里,結構體變量寫在.h文件里,.h文件里想用.c聲明的變量得加extern

如.h文件里:

typedef struct
{
? ? uint16_t ecd;
? ? int16_t speed_rpm;
? ? int16_t given_current;
? ? uint8_t temperate;
? ? int16_t last_ecd;
} motor_measure_t;//電機數(shù)據(jù)結構體

.c文件里:

motor_measure_t motor_chassis[7];

那么.h文件里:

extern motor_measure_t motor_chassis[7];

舵機

開啟定時器 開啟定時器的pwm模式 獲取遙控器數(shù)據(jù)指針 設置舵機對應引腳的初電平值(剛開始轉(zhuǎn)過的角度) 當遙控器對應的寄存器值為多少時設置不同的電平,即舵機轉(zhuǎn)過的角度。

PID

//位置式pid float pidUpdate(PidObject* pid, const float error) {float output;pid->error = error;if(abs(pid->error)<pid->Limit){pid->integ += pid->error; //誤差累加,積分項,iif (pid->integ > pid->iLimit){pid->integ = pid->iLimit;//限幅}else if (pid->integ < pid->iLimitLow){pid->integ = pid->iLimitLow;//限幅}}pid->deriv = pid->error - pid->prevError;//此次誤差減去上次誤差,微分項,dpid->outP = pid->kp * pid->error;pid->outI = pid->ki * pid->integ;pid->outD = pid->kd * pid->deriv;output = pid->outP + pid->outI + pid->outD;pid->prevError = pid->error;return output; }void chassis_pid(void) {motor.Set_motor_speed[0] = 200;//(rc_ctrl.rc.ch[3]+rc_ctrl.rc.ch[2]+rc_ctrl.rc.ch[0])*3*rc_ctrl.rc.s[0]-spin*98; motor.Actual_motor_speed[0] = motor_chassis[0].speed_rpm; motor.Out_motor_speed[0] = pidUpdate(&motor.pid_motor_speed[0] , motor.Set_motor_speed[0] - motor.Actual_motor_speed[0]);motor.Out_motor_speed[0] = limit_ab(motor.Out_motor_speed[0],-20000,20000);CAN_cmd_chassis(motor.Out_motor_speed[0],motor.Out_motor_speed[1],motor.Out_motor_speed[2],motor.Out_motor_speed[3]); }主函數(shù)中 : init_pid_all();//pid初始化 while(1) {chassis_pid();//不斷更新pid并給電機發(fā)送數(shù)據(jù)HAL_Delay(10); } //pid結構體 typedef struct {float error;float integ;float iLimit;float iLimitLow;float Limit;float deriv;float outP;float outI;float outD;float kp;float ki;float kd;float prevError; } PidObject;//電機結構體 typedef struct {s16 Out_Car_turn;s16 Set_motor_speed[8];s16 Actual_motor_speed[8]; s16 Out_motor_speed[8];s16 Out_Claw_Round[2];s16 Actual_Claw_round[2];PidObject pid_motor_speed[8];//電機轉(zhuǎn)速的結構體變量PidObject pid_car_turn;PidObject pid_claw_round[2];PidObject pid_x_speed;PidObject pid_z_speed;PidObject pid_motor[4];} Motor;

十六. IMU 溫度控制

使用 PID 控制算法對 IMU 進行溫度控制

零漂現(xiàn)象是指當物理量輸入為零,傳感器測量的輸出量不為零的現(xiàn)象。即 IMU 沒有任何運動,陀螺儀和加速度計(這倆都是傳感器)也會讀取到一定大小的數(shù)據(jù),并將其當作是由 IMU 運動產(chǎn)生的。

PID:𝑈(𝑡) = 𝐾𝑝 ? 𝑒𝑟𝑟(𝑡) + 𝐾𝑖 ? ∫ 𝑒𝑟𝑟(𝑡)𝑑𝑡 + 𝐾𝑑 ? 𝑑𝑒𝑟𝑟(𝑡)/?𝑑𝑡

err(t)即誤差值,Kp,Ki,Kd 分別為比例,積分,微分三項的系數(shù)。

單片機系統(tǒng)是離散的,所以位置式pid公式:𝑢(𝑘) = 𝐾𝑝 ? 𝑒(𝑘) + 𝐾𝑖 ? ∑(i=0到k)𝑒(𝑖) ?+ 𝐾𝑑 ? [𝑒(𝑘) ? 𝑒(𝑘 ? 1)];

增量式pid公式:𝛥𝑢(𝑘) = 𝐾𝑝 ? [𝑒(𝑘) ? 𝑒(𝑘 ? 1)] + 𝐾𝑖 ? 𝑒(𝑘) + 𝐾𝑑 ? [𝑒(𝑘) ? 2 ? 𝑒(𝑘 ? 1) + 𝑒(𝑘 ? 2)]

實際上也可以理解為我們在t0、t1、t2...每隔相等的時間(如1s)進行一次采樣,計算這一次輸出量的值和上一次的值作為誤差。

pid算法中,以比例環(huán)節(jié)為例,控制器=k*eu,而非最終得到的輸出量=k*eu。例如我們給水壺加熱,那么最終需要的量是溫度,但是pid控制的是加熱功率:如我們要加熱到80度,現(xiàn)在75度,則eu=5,加熱功率就是5k,然后溫度上升;當加熱功率小于散熱的時候溫度下降,通過這種改變加熱功率的方式才是pid調(diào)節(jié)。

以我們的車子追蹤一個會動的物體為例:error為車子和物體之間的距離。比例算法中,由于物體在動,距離增大,那么error增大,控制車子速度的量增大,某一瞬間to車速等于物體速度,設此時距離為x0;此后車子和物體速度相同,error不變,那么它們將會永遠間隔這樣一個距離,稱為穩(wěn)態(tài)誤差。

引入積分算法后,控制車子速度的量不僅是error乘上比例系數(shù)k,還有error對時間的積分(乘以積分系數(shù))。t0時刻車速等于物體速度,由于時間永遠在增加,所以積分項一直增加,車子速度肯定會增加,那么車子將繼續(xù)追趕物體;此后車子與物體的距離減小,那么比例項的作用減弱,而積分項的作用一直增加,直到某一刻它們的作用扯平使得車子速度又等于物體速度(所以車子速度在第一次相等后經(jīng)歷了增加再減小的過程);然后發(fā)現(xiàn)由于時間增加積分項又增加,比例項又減小,無線逼近。

若振蕩(車子超過物體了),采用微分控制。控制變化曲線的斜率。

十七 底盤控制任務

主文件: can_filter_init(); init_pid_all();//pid的ki、kp、kd等參數(shù)初始化 remote_control_init(); RC_ctrl_t * local_rc_ctrl = get_remote_control_point(); while(1) {chassis_pid();//pid參數(shù)更新,并將更新的數(shù)據(jù)(電流值、轉(zhuǎn)速等)發(fā)送HAL_Delay(10); }pid.c文件: float pidUpdate(PidObject* pid, const float error) //pid參數(shù)更新函數(shù),輸入變量為pid結構體和error {error賦值到pid結構體內(nèi)部的error中去誤差累加得到積分項對積分項進行限幅德塔誤差=上次誤差-這次誤差,得到微分項pid->outP = pid->kp * pid->error;pid->outI = pid->ki * pid->integ;pid->outD = pid->kd * pid->deriv;output = pid->outP + pid->outI + pid->outD;//更新pid參數(shù)并返回輸出值上次誤差=這次誤差返回輸出值 } void chassis_pid(void) //調(diào)用更新的pid參數(shù)輸出值,并發(fā)送到電機 {vx=local_rc_ctrl->rc.ch[0]/6.6*50;//車速與遙控器成正比vy=-local_rc_ctrl->rc.ch[1]/6.6*50;wz=-local_rc_ctrl->rc.ch[2]/6.6*50;v1=-vx-vy-wz;v2=vx-vy+wz;v3=vx+vy-wz;v4=-vx+vy+wz;/* 底盤四個電機的PID計算并輸出 *///電機1:motor.Set_motor_speed[0] = v1; motor.Actual_motor_speed[0] = motor_chassis[0].speed_rpm; motor.Out_motor_speed[0] = pidUpdate(&motor.pid_motor_speed[0] , motor.Set_motor_speed[0] - motor.Actual_motor_speed[0]);//更新后的電流值賦值給motor.Out_motor_speed變量,輸入是轉(zhuǎn)速,輸出motor.Out_motor_speed變量是電流值。motor.Out_motor_speed[0] = limit_ab(motor.Out_motor_speed[num],-10000,10000); //對電流值進行限幅CAN_cmd_chassis(motor.Out_motor_speed[0],motor.Out_motor_speed[1],motor.Out_motor_speed[2],motor.Out_motor_speed[3]); //將得到的電流值發(fā)送給各個電機 }

總結

以上是生活随笔為你收集整理的stm32f407igh6学习笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。