須知
開發(fā)板:STM32F103C8T6最小系統(tǒng)板
編譯環(huán)境:Keil5 MDK
輔助軟件:STM32 CubeMX
課程教學(xué):基于正點(diǎn)原子HAL庫學(xué)習(xí)教程
其余配件:江科大STM32配件包?和 示波器一臺(tái)
備注:?因?yàn)檫@塊開發(fā)板沒有基本定時(shí)器,所以本文也沒有基本定時(shí)器的內(nèi)容
????????????本文1.3和2.1部分的標(biāo)題不知道為什么顯示不對
? ? ? ? ? ? 大家湊合一下應(yīng)該還是看得懂標(biāo)題的
一. 通用定時(shí)器
1.1 定時(shí)器中斷
1.1.1 實(shí)現(xiàn)目標(biāo)
我們這里目標(biāo)為用定時(shí)器2實(shí)現(xiàn)LED以500ms為間隔亮滅
注意LED接PA6
1.1.2 CubeMX配置
?配置系統(tǒng)線
配置時(shí)鐘
這里用高速就行了,所以低速就不設(shè)置了
配置GPIOA6
?配置定時(shí)器2
注意Psc和Arr都需要減1所以應(yīng)該設(shè)置為7199和4999? ? ?
配置NVIC
?設(shè)置時(shí)鐘頻率并生成工程
這里有個(gè)便捷方法,就是在紅框里面輸入72,然后按下enter,再點(diǎn)擊OK
這樣CubeMX就會(huì)幫我們自動(dòng)配好其他的
這樣我們的CubeMX就寫好了,接下來是代碼部分
1.1.3 程序編寫
這里我們要先在主函數(shù)中給中斷使能
HAL_TIM_Base_Start_IT(&htim2); //使能中斷
然后我們在主函數(shù)下面加上
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2) //判斷是否為定時(shí)器2產(chǎn)生的中斷
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6);//GPIOA6電平翻轉(zhuǎn)
}
}
再然后我們編譯并下載程序,我們就會(huì)發(fā)現(xiàn)在PA6上的LED以500ms為間隔亮滅了
1.2 定時(shí)器輸入比較(PWM)
1.2.1 實(shí)現(xiàn)目標(biāo)
我們的目標(biāo)是實(shí)現(xiàn)LED的呼吸燈,同時(shí)我們在旁邊點(diǎn)亮一個(gè)LED來作為對比
注意一個(gè)LED接PA0,另一個(gè)LED接PA6
1.2.2 CubeMX配置
這里的配置和上面的定時(shí)器中斷開始和結(jié)尾是一樣的
所以看過上面部分的朋友可以直接看定時(shí)器部分的配置
但需要注意這里不用配置NVIC
配置系統(tǒng)線
配置時(shí)鐘
這里用高速就行了,所以低速就不設(shè)置了
配置GPIOA6
配置定時(shí)器2
這里附上一張總的引腳定義圖,可以作為參考
接了下來我們配置定時(shí)器2 PWM
具體原理我就不講解了,不懂的去百度都有
設(shè)置時(shí)鐘頻率并生成工程
這里有個(gè)便捷方法,就是在紅框里面輸入72,然后按下enter,再點(diǎn)擊OK
這樣CubeMX就會(huì)幫我們自動(dòng)配好其他的
這樣我們的CubeMX就寫好了,接下來是代碼部分
1.2.3 程序編寫
首先我們需要定義一個(gè)全局變量
uint16_t Pwm; //PWM控制
記下來我們在主程序while前面加入這句代碼
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //開啟 定時(shí)器2 PWM通道1
然后我們在主程序while循環(huán)中加入下面的代碼
while (Pwm < 999)
{
Pwm++;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, Pwm); //設(shè)置比較值
HAL_Delay(1); //延時(shí)1ms不然改變太快
}
while (Pwm)
{
Pwm--;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, Pwm); //設(shè)置比較值
HAL_Delay(1); //延時(shí)1ms不然改變太快
}
接下來我們編譯下載就能看到LED呼吸燈的效果了
1.3 定時(shí)器輸入捕獲
1.3.1 實(shí)現(xiàn)目標(biāo)
我們的實(shí)現(xiàn)目標(biāo)為:按鍵下,松手后將脈寬值通過串口發(fā)送給電腦,并顯示在OLED上
1.3.2 CubeMX配置
配置系統(tǒng)線
配置時(shí)鐘
這里用高速就行了,所以低速就不設(shè)置了
配置定時(shí)器
配置GPIO
配置I2C
配置串口
這里開不開中斷都無所謂,因?yàn)槲覀冎话l(fā)送數(shù)據(jù)
設(shè)置時(shí)鐘頻率并生成工程
這里有個(gè)便捷方法,就是在紅框里面輸入72,然后按下enter,再點(diǎn)擊OK
這樣CubeMX就會(huì)幫我們自動(dòng)配好其他的
這樣我們的CubeMX就寫好了,接下來是代碼部分
1.3.3 程序編寫
usart.c
我們將使用串口的重定向
首先包含#include <stdio.h>
然后在末尾加上
int fputc(int ch, FILE * f)
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xFFFF);
return ch;
}
注意我們還需要設(shè)置一下
首先我們要打開魔法棒,然后勾上就行了
main.c
首先一樣是包含頭文件
#include <stdio.h>
#include "./OLED/OLED.h"
然后在include下面加上
/* 輸入捕獲狀態(tài)(g_timxchy_cap_sta)
* [7] :0,沒有成功的捕獲;1,成功捕獲到一次.
* [6] :0,還沒捕獲到高電平;1,已經(jīng)捕獲到高電平了.
* [5:0]:捕獲高電平后溢出的次數(shù),最多溢出63次,所以最長捕獲值 = 63*65536 + 65535 = 4194303
* 注意:為了通用,我們默認(rèn)ARR和CCRy都是16位寄存器,對于32位的定時(shí)器(如:TIM1),也只按16位使用
* 按1us的計(jì)數(shù)頻率,最長溢出時(shí)間為:4194303 us, 約4.19秒
*
* (說明一下:正常32位定時(shí)器來說,1us計(jì)數(shù)器加1,溢出時(shí)間:4.294秒)
*/
uint8_t g_timxchy_cap_sta = 0; /* 輸入捕獲狀態(tài) */
uint16_t g_timxchy_cap_val = 0; /* 輸入捕獲值 */
接著在主函數(shù)里定義一個(gè)變量
uint32_t temp = 0;
再然后我們在while循環(huán)內(nèi)加入
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
OLED_ShowString(0, 4, "Down", 16, 0);
else
OLED_ShowString(0, 4, " UP ", 16, 0);
if (g_timxchy_cap_sta & 0X80) /* 成功捕獲到了一次高電平 */
{
temp = g_timxchy_cap_sta & 0X3F;
temp *= 65536; /* 溢出時(shí)間總和 */
temp += g_timxchy_cap_val; /* 得到總的高電平時(shí)間 */
g_timxchy_cap_sta = 0; /* 開啟下一次捕獲*/
OLED_ShowNum(32, 0, temp, 7, 16, 0);
printf("LOW: %4d ms", temp/1000);
}
再然后我們在主函數(shù)下面加上回調(diào)函數(shù)
/* 定時(shí)器輸入捕獲回調(diào)函數(shù) */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
if ((g_timxchy_cap_sta & 0X80) == 0) /* 還未成功捕獲 */
{
if (g_timxchy_cap_sta & 0X40) /* 捕獲到一個(gè)下降沿 */
{
g_timxchy_cap_sta |= 0X80; /* 標(biāo)記成功捕獲到一次高電平脈寬 */
g_timxchy_cap_val = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); /* 獲取當(dāng)前的捕獲值 */
TIM_RESET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1); /* 一定要先清除原來的設(shè)置 */
TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); /* 配置TIM2通道1上升沿捕獲 */
}
else /* 還未開始,第一次捕獲上升沿 */
{
g_timxchy_cap_sta = 0; /* 清空 */
g_timxchy_cap_val = 0;
g_timxchy_cap_sta |= 0X40; /* 標(biāo)記捕獲到了上升沿 */
__HAL_TIM_SET_COUNTER(&htim2, 0); /* 定時(shí)器2計(jì)數(shù)器清零 */
TIM_RESET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1); /* 一定要先清除原來的設(shè)置?。?*/
TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); /* 定時(shí)器2通道1設(shè)置為下降沿捕獲 */
}
}
}
}
/* 定時(shí)器更新中斷回調(diào)函數(shù) */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
if ((g_timxchy_cap_sta & 0X80) == 0) /* 還未成功捕獲 */
{
if (g_timxchy_cap_sta & 0X40) /* 已經(jīng)捕獲到高電平了 */
{
if ((g_timxchy_cap_sta & 0X3F) == 0X3F) /* 高電平太長了 */
{
TIM_RESET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1); /* 一定要先清除原來的設(shè)置 */
TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING);/* 配置TIM2通道1上升沿捕獲 */
g_timxchy_cap_sta |= 0X80; /* 標(biāo)記成功捕獲了一次 */
g_timxchy_cap_val = 0XFFFF;
}
else /* 累計(jì)定時(shí)器溢出次數(shù) */
{
g_timxchy_cap_sta++;
}
}
}
}
}
再然后我們就可以下載編譯就能實(shí)現(xiàn)現(xiàn)象了
二. 高級定時(shí)器
2.1 定時(shí)器輸出多路PWM
2.1.1 實(shí)現(xiàn)目標(biāo)
用定時(shí)器1產(chǎn)生多路PWM實(shí)現(xiàn)LED的不同亮度做對比
這里因?yàn)樵O(shè)備問題拍照看不出來,但實(shí)物還是呢看出來的
2.1.2 CubeMX配置
?配置系統(tǒng)線
配置時(shí)鐘
這里用高速就行了,所以低速就不設(shè)置了
配置定時(shí)器
設(shè)置時(shí)鐘頻率并生成工程
這里有個(gè)便捷方法,就是在紅框里面輸入72,然后按下enter,再點(diǎn)擊OK
這樣CubeMX就會(huì)幫我們自動(dòng)配好其他的
這樣我們的CubeMX就寫好了,接下來是代碼部分
2.1.3 程序編寫
這里的很簡單
在while循環(huán)前開啟PWM就行
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
然后就是編譯下載就完成了
2.2 定時(shí)器OC輸出比較產(chǎn)生相位差
2.2.1 實(shí)現(xiàn)目標(biāo)
使用定時(shí)器1產(chǎn)生4路占空比為50%的PWM并具有相位差
相位差為45度
相位差為90度
相位差為135度
2.2.2 CubeMX配置
??配置系統(tǒng)線
配置時(shí)鐘
這里用高速就行了,所以低速就不設(shè)置了
配置定時(shí)器
?設(shè)置時(shí)鐘頻率并生成工程
這里有個(gè)便捷方法,就是在紅框里面輸入72,然后按下enter,再點(diǎn)擊OK
這樣CubeMX就會(huì)幫我們自動(dòng)配好其他的
這樣我們的CubeMX就寫好了,接下來是代碼部分
2.2.3 程序編寫
我們需要使能輸出捕獲所以在while循環(huán)之前加入這段代碼
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_2);
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_3);
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_4);
然后我們就可以編譯下載了
2.3 定時(shí)器OC互補(bǔ)輸出
2.3.1 實(shí)現(xiàn)目標(biāo)
OC互補(bǔ)輸出用2個(gè)通道產(chǎn)生4路占空比為50%的PWM
具體原理我這里不多贅述,可以去看正點(diǎn)原子的HAL庫教學(xué)視頻
2.3.2 CubeMX配置
?配置系統(tǒng)線
配置時(shí)鐘
這里用高速就行了,所以低速就不設(shè)置了
配置定時(shí)器
??設(shè)置時(shí)鐘頻率并生成工程
這里有個(gè)便捷方法,就是在紅框里面輸入72,然后按下enter,再點(diǎn)擊OK
這樣CubeMX就會(huì)幫我們自動(dòng)配好其他的
這樣我們的CubeMX就寫好了,接下來是代碼部分
2.3.3 程序編寫
我們只需要在while循環(huán)前面加上使能OC和OCN的代碼就行了
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_OCN_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_OCN_Start(&htim1, TIM_CHANNEL_2);
然后我們就可以編譯下載了
2.4 定時(shí)器PWM互補(bǔ)輸出加死區(qū)生成
2.4.1 實(shí)現(xiàn)目標(biāo)
開啟兩路的PWM和其互補(bǔ)輸出,并有死區(qū)生成
2.4.2 CubeMX配置
??配置系統(tǒng)線
配置時(shí)鐘
這里用高速就行了,所以低速就不設(shè)置了
配置定時(shí)器
??設(shè)置時(shí)鐘頻率并生成工程
這里有個(gè)便捷方法,就是在紅框里面輸入72,然后按下enter,再點(diǎn)擊OK
這樣CubeMX就會(huì)幫我們自動(dòng)配好其他的
這樣我們的CubeMX就寫好了,接下來是代碼部分
2.4.3 程序編寫
我們只需要在while前加上
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);
然后我們就可以編譯下載了
2.5 定時(shí)器PWM輸入捕獲
2.5.1 實(shí)現(xiàn)目標(biāo)
用定時(shí)器1對定時(shí)器2產(chǎn)生的PWM測量,并用串口打印出來
有興趣的可以用示波器測 ,我這里就不展示效果圖了
注意在不考慮溢出的情況下這里的配置測量周期最大為910us
周期為100us,高電平脈寬為60us
周期為910us,高電平脈寬為60us
周期為911us(注意已經(jīng)超出),高電平脈寬為60us
明顯看出周期不對
2.5.2 CubeMX配置
?配置系統(tǒng)線
配置時(shí)鐘
這里用高速就行了,所以低速就不設(shè)置了
配置定時(shí)器1
配置定時(shí)器2
配置串口
這里開不開中斷都無所謂,因?yàn)槲覀冎话l(fā)送數(shù)據(jù)
??設(shè)置時(shí)鐘頻率并生成工程
這里有個(gè)便捷方法,就是在紅框里面輸入72,然后按下enter,再點(diǎn)擊OK
這樣CubeMX就會(huì)幫我們自動(dòng)配好其他的
這樣我們的CubeMX就寫好了,接下來是代碼部分
2.5.3 程序編寫
?usart.c
我們將使用串口的重定向
首先包含#include <stdio.h>
然后在末尾加上
int fputc(int ch, FILE * f)
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xFFFF);
return ch;
}
注意我們還需要設(shè)置一下
首先我們要打開魔法棒,然后勾上就行了
main.c
首先我們在前創(chuàng)建3個(gè)全局變量
uint8_t Tim1_IC_Flag; //0-未捕獲 1-捕獲
uint16_t Tim2_Pwm_H_Val; //PWM的高電平脈寬
uint16_t Tim2_Pwm_C_Val; //PWM的周期寬度
然后再主函數(shù)內(nèi)創(chuàng)建4個(gè)局部變量
double ht, ct, f, tpsc;
再就是在while循環(huán)前添加代碼
// 定時(shí)器2PWM使能 產(chǎn)生PWM波
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
//定時(shí)器1輸入捕獲使能
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1);
HAL_TIM_IC_Start(&htim1, TIM_CHANNEL_2);
如果我們要修改定時(shí)器2通道1的比較值和定時(shí)器的重裝載值可以用這個(gè)函數(shù)和宏定義
//修改參數(shù)
TIM2->CCR1 = 60;
__HAL_TIM_SetAutoreload(&htim2, 100);
再然后我們就可以寫while循環(huán)內(nèi)的邏輯了
HAL_Delay(500); //延時(shí)500ms不然打印數(shù)據(jù)太快
if (Tim1_IC_Flag) /* 捕獲了一次數(shù)據(jù) */
{
printf("\r\n"); /* 輸出空,另起一行 */
printf("PWM Hight:%d\r\n", Tim2_Pwm_H_Val); /* 打印高電平脈寬 */
printf("PWM Cycle:%d\r\n", Tim2_Pwm_C_Val); /* 打印周期 */
tpsc = ((double)0 + 1) / 72; /* 得到PWM采樣時(shí)鐘周期時(shí)間 */
ht = Tim2_Pwm_H_Val * tpsc; /* 計(jì)算高電平時(shí)間 */
ct = Tim2_Pwm_C_Val * tpsc; /* 計(jì)算周期長度 */
f = (1 / ct) * 1000000; /* 計(jì)算頻率 */
printf("PWM Hight time:%.3fus\r\n", ht); /* 打印高電平脈寬長度 */
printf("PWM Cycle time:%.3fus\r\n", ct); /* 打印周期時(shí)間長度 */
printf("PWM Frequency :%.3fHz\r\n", f); /* 打印頻率 */
HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_1); //停止定時(shí)器中斷
Tim1_IC_Flag = 0;/* 清零狀態(tài),重新開始檢測 */
Tim2_Pwm_H_Val=0;
Tim2_Pwm_C_Val=0;/* 重啟PWM輸入檢測 */
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1); //開啟定時(shí)器中斷
}
接下來就是在主函數(shù)后面寫定時(shí)器輸入捕獲中斷回調(diào)函數(shù)了
/* 定時(shí)器輸出捕獲中斷回調(diào)函數(shù) */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM1) //判斷是否為定時(shí)器1中斷
{
if (Tim1_IC_Flag == 0) //未捕獲
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) //判斷是否是通道一
{
Tim2_Pwm_H_Val = HAL_TIM_ReadCapturedValue(&htim1, TIM_CHANNEL_2) + 1 + 1; //讀取高脈寬
Tim2_Pwm_C_Val = HAL_TIM_ReadCapturedValue(&htim1, TIM_CHANNEL_1) + 1 + 1; //讀取周期
Tim1_IC_Flag = 1; //標(biāo)志位置1
}
}
}
}
然后我們就可以編譯下載看現(xiàn)象了,大家也可以多嘗試PWM看看效果
注意上面實(shí)現(xiàn)目標(biāo)的內(nèi)容就行了
三、結(jié)束語
這篇文章主要也是我自己為了我自己復(fù)習(xí)用的筆記
所以沒有過多的原理解釋,寫得也一般
主要是因?yàn)檎c(diǎn)原子并沒有出配置CubeMX的教程
所以我寫了這個(gè),原理部分看正點(diǎn)原子就行了文章來源:http://www.zghlxwxcb.cn/news/detail-859151.html
希望大家越學(xué)越有,早日成為嵌入式巨佬文章來源地址http://www.zghlxwxcb.cn/news/detail-859151.html
到了這里,關(guān)于STM32 HAL庫 CubeMX配置 定時(shí)器學(xué)習(xí) F103C8T6的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!