.net網(wǎng)站開(kāi)發(fā)步驟免費(fèi)軟文網(wǎng)站
STM32中微秒延時(shí)的實(shí)現(xiàn)方式
- 0.前言
- 一、裸機(jī)實(shí)現(xiàn)方式
- 二、FreeRTOS實(shí)現(xiàn)方式
- 三、定時(shí)器實(shí)現(xiàn)(通用)
- 4、總結(jié)
0.前言
??最近在STM32驅(qū)動(dòng)移植過(guò)程中需要用到微秒延時(shí)來(lái)實(shí)現(xiàn)一些外設(shè)的時(shí)序,由于網(wǎng)上找到的驅(qū)動(dòng)方法良莠不齊,筆者在實(shí)現(xiàn)時(shí)序過(guò)程中也浪費(fèi)了不少時(shí)間。這里就將筆者覺(jué)得比較好的幾種方式記錄一下,方便后續(xù)使用,也可以作為參考。
一、裸機(jī)實(shí)現(xiàn)方式
??在STM32的裸機(jī)程序中,實(shí)現(xiàn)微秒延時(shí)比較簡(jiǎn)單,通過(guò)SysTick計(jì)時(shí)即可。關(guān)于SysTick的相關(guān)知識(shí)可以站內(nèi)搜索,這里就不再過(guò)多贅述了,相關(guān)的delay_us函數(shù)參考了正點(diǎn)原子例程中的實(shí)現(xiàn)方式:
// 注意:nus的值,不要大于798915us(最大值即2^24/fac_us {fac_us=21})
void delay_us(uint32_t nus)
{uint32_t temp;SysTick->LOAD = nus * fac_us; // 時(shí)間加載,fac_us與時(shí)鐘頻率有關(guān),例如:168Mhz時(shí)// fac_us=168,168/168M=1usSysTick->VAL = 0x00; // 清空計(jì)數(shù)器SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 開(kāi)始倒數(shù)do{temp = SysTick->CTRL; // 讀取控制及狀態(tài)寄存器的值} while ((temp & 0x01) && !(temp & (1 << 16))); // 等待時(shí)間到達(dá),使能且位16為0(未計(jì)到0)SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 關(guān)閉計(jì)數(shù)器SysTick->VAL = 0X00; // 清空計(jì)數(shù)器
}
步驟:
1、根據(jù)延時(shí)時(shí)間和定時(shí)器所選時(shí)鐘頻率,計(jì)算出定時(shí)器要計(jì)數(shù)的時(shí)間數(shù)值;
2、將該數(shù)值加載到重裝載寄存器中;
3、將當(dāng)前值寄存器清零,打開(kāi)定時(shí)器開(kāi)始計(jì)數(shù);
4、等待控制及狀態(tài)寄存器的位16變?yōu)?;
5、關(guān)閉定時(shí)器,退出。
二、FreeRTOS實(shí)現(xiàn)方式
在FreeRTOS中,SysTick定時(shí)器的重裝載值是固定的,且定時(shí)器是一直在工作的。因此像裸機(jī)編程中將時(shí)間數(shù)值加載到重裝載寄存器的方法不再適用。經(jīng)過(guò)多方的查找和修改,最終使用的以下的實(shí)現(xiàn)方式:
#include "main.h"
#include "FreeRTOSConfig.h"extern void vPortSetupTimerInterrupt( void );void delay_us(uint32_t nus)
{uint32_t ticks;uint32_t told, tnow, reload, tcnt = 0;if ((0x0001 & (SysTick->CTRL)) == 0) { // 定時(shí)器未工作vPortSetupTimerInterrupt(); // 初始化定時(shí)器}reload = SysTick->LOAD; // 獲取重裝載寄存器值ticks = nus * (SystemCoreClock / 1000000); // 計(jì)數(shù)時(shí)間值told = SysTick->VAL; // 獲取當(dāng)前數(shù)值寄存器值(開(kāi)始時(shí)數(shù)值)while (1){tnow = SysTick->VAL; // 獲取當(dāng)前數(shù)值寄存器值if (tnow != told) // 當(dāng)前值不等于開(kāi)始值說(shuō)明已在計(jì)數(shù){if (tnow < told) { // 當(dāng)前值小于開(kāi)始數(shù)值,說(shuō)明未計(jì)到0tcnt += told - tnow; // 計(jì)數(shù)值=開(kāi)始值-當(dāng)前值} else { // 當(dāng)前值大于開(kāi)始數(shù)值,說(shuō)明已計(jì)到0并重新計(jì)數(shù)tcnt += reload - tnow + told; // 計(jì)數(shù)值=重裝載值-當(dāng)前值+開(kāi)始值 (已從開(kāi)始值計(jì)到0)}told = tnow; // 更新開(kāi)始值if (tcnt >= ticks) {break; // 時(shí)間超過(guò)/等于要延遲的時(shí)間,則退出.}}}
}// SystemCoreClock為系統(tǒng)時(shí)鐘(system_stmf1xx.c中),通常選擇該時(shí)鐘作為systick定時(shí)器時(shí)鐘,根據(jù)具體情況更改
步驟:
1、根據(jù)延時(shí)時(shí)間和定時(shí)器所選時(shí)鐘頻率,計(jì)算出定時(shí)器要計(jì)數(shù)的時(shí)間數(shù)值;
2、獲取當(dāng)前數(shù)值寄存器的數(shù)值;
3、以當(dāng)前數(shù)值為基準(zhǔn)開(kāi)始計(jì)數(shù);
4、當(dāng)所計(jì)數(shù)值等于(大于)需要延時(shí)的時(shí)間數(shù)值時(shí)退出。
注:由于筆者使用STM32CubeIDE中通過(guò)CMSIS封裝后的FreeRTOS API,所以需要額外extern引入與初始化定時(shí)器相關(guān)的API。如果使用原生FreeRTOS API,則需要根據(jù)情況引入。
三、定時(shí)器實(shí)現(xiàn)(通用)
??這種方式的優(yōu)點(diǎn)就是簡(jiǎn)單且易用,直接使用一個(gè)閑置定時(shí)器分頻后即可。在CubeMX中配制定時(shí)器,筆者選擇基本定時(shí)器 TIM7,預(yù)分頻PSC設(shè)置為(TIM總線時(shí)鐘 - 1),分頻后頻率為1MHz,即每次計(jì)數(shù)累加1/1000000秒 = 1us,計(jì)數(shù)區(qū)間65535,單次定時(shí)最長(zhǎng)65535微秒,計(jì)數(shù)模式為up 向上計(jì)數(shù),是否自動(dòng)重新加載選disable,一次計(jì)數(shù)滿后不再重新計(jì)數(shù)。
void delay_us(uint16_t nus)
{__HAL_TIM_SetCounter(&htim7,0);__HAL_TIM_ENABLE(&htim7);while(__HAL_TIM_GetCounter(&htim7)<nus);__HAL_TIM_DISABLE(&htim7);
}
步驟:
1、首先對(duì)定時(shí)器中的計(jì)數(shù)器進(jìn)行置0;
2、使能定時(shí)器;
3、循環(huán)等待;
4、關(guān)閉定時(shí)器退出;
4、總結(jié)
??由于微秒延時(shí)間隔較短,所以在FreeRTOS中使用時(shí)最好避免內(nèi)核對(duì)該線程進(jìn)行調(diào)度,否則調(diào)度到其他線程可能要等到下一個(gè)時(shí)間片才能恢復(fù)運(yùn)行。筆者對(duì)這部分還沒(méi)有仔細(xì)了解,如有條件最好還是對(duì)相關(guān)線程添加關(guān)閉和恢復(fù)線程調(diào)度的操作。