PWM是一種應(yīng)用廣泛的利用微處理器的數(shù)字輸出來對模擬電路進(jìn)行控制的一種技術(shù)(即對脈沖寬度的控制)PWM同時(shí)也是驅(qū)動(dòng)蜂鳴器,驅(qū)動(dòng)舵機(jī),通信等重要的一環(huán),而對于初學(xué)者而言,點(diǎn)完燈的下一個(gè)程序就是驅(qū)動(dòng)蜂鳴器,本篇將講述如何使用及調(diào)整PWM輸出頻率,占空比
工程文件可入Q群:659512171獲取
?PWM簡介:
對于STM32,PWM輸出依靠定時(shí)器,而在STM32F103c8t6中,共有4個(gè)定時(shí)器可供輸出,每個(gè)都可以配置4路輸出,所以總共可以輸出16路PWM,本篇只介紹和應(yīng)用單路PWM輸出。
工程開始:
新建工程:
打開STM32CubeMX,新建工程,配置外部高速時(shí)鐘:
?
?輸入需要的頻率,敲擊回車,STM32CubeMX會(huì)自動(dòng)配置(在這里我把時(shí)鐘頻率設(shè)為了最高72,但其實(shí)不設(shè)也行,在后面的分頻改一下就好了)
配置調(diào)試:
這里一定要選好,不然會(huì)導(dǎo)致芯片自鎖
配置工程文件:
配置計(jì)時(shí)器:
?下面對這其中的3個(gè)值進(jìn)行說明:
預(yù)分頻(psc):CPU運(yùn)行頻率先經(jīng)過它分頻再進(jìn)入計(jì)時(shí)器,如CPU運(yùn)行在 x Mhz 下,預(yù)分頻為?y,那進(jìn)入計(jì)時(shí)器的頻率也就為 x/(y+1) Mhz(因?yàn)閺?計(jì)數(shù),所以是y+1)。
自動(dòng)重裝值(arr):指一次周期的計(jì)數(shù)長度
脈沖長度:指輸出脈沖的計(jì)數(shù)長度
在這里就可以知道怎么調(diào)節(jié)頻率于占空比了,首先,在預(yù)分頻(y)和主頻(x)一定的情況下,要保持輸出 a% 占空比的?b Hz 的方波,那么自動(dòng)重裝值就應(yīng)該是 x / (y+1) /?b ,而脈沖長度也就應(yīng)該是 (x / (y+1) / b) * a%,所以只需要寫一個(gè)函數(shù)調(diào)節(jié)自動(dòng)重裝值和脈沖長度就可以實(shí)現(xiàn)調(diào)節(jié)頻率和占空比的目的了。
這里我分別把預(yù)分頻、自動(dòng)重裝值、脈沖長度設(shè)置為了 999、1440、720,所以頻率和占空比分別應(yīng)為:72,000,000 / (999+1)/1440 = 50 (Hz),1440?/ 720 = 50%
然后點(diǎn)擊右上角的GENERATE CODE生成文件,然后打開。
Keil配置:?
頻率占空比調(diào)節(jié)方式一:
?打開main.c,tim.c,tim.h文件:
( .h文件可以在引用中通過右鍵打開)
找到 "MX_TIM2_Init"?函數(shù)
?可以看到我們設(shè)置的參數(shù)都在這個(gè)初始化函數(shù)里了,接下來只需要設(shè)置2個(gè)變量替換其中2個(gè)具體值。
將上面這一段代碼復(fù)制到下面的? /* USER CODE BEGIN 1 */? 和? /* USER CODE END 1 */? 區(qū)間里,并把上面這一段代碼注釋掉(刪掉也可以),
接著,更改一下后面括號中的數(shù)據(jù)(原本是?void,無類型,現(xiàn)在要換成數(shù)據(jù)),只需要設(shè)置自動(dòng)重裝值和脈沖長度的變量:
/* USER CODE BEGIN 1 */
void MX_TIM2_Init(uint16_t arr, uint16_t pul)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 1000;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = arr;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = pul;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
/* USER CODE END 1 */
另外,還需要在tim.h中更改對此函數(shù)的聲明,不然是報(bào)錯(cuò)不通過的,也無法在主程序中引用,同樣把函數(shù)聲明移到用戶區(qū)域里,并注釋掉:
/* USER CODE BEGIN Prototypes */
void MX_TIM2_Init(uint16_t arr, uint16_t pul);
/* USER CODE END Prototypes */
?如果你覺得每次都寫?HAL_TIM_PWM_Start 和 HAL_TIM_PWM_Stop?太麻煩,可以再加一個(gè)PWM開啟/關(guān)閉函數(shù),同樣是在用戶區(qū)域?qū)懭缦潞瘮?shù):
void PWM (int onoff)
{
if (onoff == 0){
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
}else if (onoff == 1){
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
}
}
?以及函數(shù)聲明:
void PWM (int onoff);
在這里當(dāng)數(shù)據(jù)為0時(shí),停止PWM,當(dāng)為1時(shí),開啟PWM?
最后,tim.c中的用戶端應(yīng)是如下代碼:
/* USER CODE BEGIN 1 */
void MX_TIM2_Init(uint16_t arr, uint16_t pul)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 1000;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = arr;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = pul;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
void PWM (int onoff)
{
if (onoff == 0){
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
}else if (onoff == 1){
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
}
}
/* USER CODE END 1 */
整個(gè)tim.h應(yīng)是如下代碼:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file tim.h
* @brief This file contains all the function prototypes for
* the tim.c file
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __TIM_H__
#define __TIM_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern TIM_HandleTypeDef htim2;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
//void MX_TIM2_Init(void);
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
/* USER CODE BEGIN Prototypes */
void MX_TIM2_Init(uint16_t arr, uint16_t pul);
void PWM (int onoff);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __TIM_H__ */
最后,在main.c中引用就行了,記得要把初始化的參數(shù)填上去,這里給出一個(gè)實(shí)例(整個(gè)int?main函數(shù))?:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init(1440,36);
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
PWM(1);
HAL_Delay(1500);
PWM(0);
MX_TIM2_Init(1440,180);
PWM(1);
HAL_Delay(1500);
PWM(0);
MX_TIM2_Init(1440,36);
}
/* USER CODE END 3 */
}
?這里先是初始化PWM輸出為50HZ,脈寬0.5ms(即2.5%占空比)的信號,然后打開PWM輸出,延時(shí)1.5秒后關(guān)閉,再配置為50Hz,脈寬2.5ms(即12.5%占空比)的信號,然后再次打開輸出,延時(shí)1.5秒后關(guān)閉并重新配置為50HZ,脈寬1ms的信號,這兩個(gè)分別是舵機(jī)的0°,180°信號,可以讓連接在tim2的一通道(即PA0)的舵機(jī)在0°到180°間重復(fù)旋轉(zhuǎn),效果如下:
相信大家也看到了,這種方式來調(diào)節(jié)頻率和占空比麻煩且會(huì)有一段時(shí)間的空缺,會(huì)影響到對波形比較敏感的設(shè)備,所以接下來介紹第二種方式
方式二:
前面過程的都與第一種方式相同,可以完全保留,第一種方式的調(diào)節(jié)函數(shù)可以作為初始化使用,此方式無需編寫任何函數(shù),調(diào)用了HAL庫中的修改CCR(儲(chǔ)存脈沖長度數(shù)值)和ARR(儲(chǔ)存自動(dòng)重裝值)寄存器的函數(shù),接下來直接上代碼:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init(1440,36);
/* USER CODE BEGIN 2 */
PWM(1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(1500);
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,180); //修改CCR寄存器
HAL_Delay(1500);
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,36);
/*
__HAL_TIM_SET_AUTORELOAD(&htim2,1440); //修改ARR寄存器,沒有通道限制,整個(gè)定時(shí)器一起改
*/
}
/* USER CODE END 3 */
}
?上面程序?qū)崿F(xiàn)效果與第一種相同,但此程序可以在PWM運(yùn)行時(shí)更改,無需關(guān)閉PWM,沒有空缺
另外,對于頻率的修改還可以通過改預(yù)分頻系數(shù)來解決,但由于自動(dòng)重裝值與預(yù)分頻之間是連除的關(guān)系,所以一般只修改自動(dòng)重裝值,修改預(yù)分頻系數(shù)的函數(shù)如下:
__HAL_TIM_SET_PRESCALER(TIM_TypeDef* TIMx, uint32_t Prescaler);
/*舉一個(gè)例子:
__HAL_TIM_SET_PRESCALER(&htim2, 1000);
這條語句就是把定時(shí)器二的預(yù)分頻系數(shù)設(shè)為1000
*/
都看到這里了點(diǎn)點(diǎn)關(guān)注再走唄文章來源:http://www.zghlxwxcb.cn/news/detail-630028.html
?交流Q群:659512171文章來源地址http://www.zghlxwxcb.cn/news/detail-630028.html
到了這里,關(guān)于STM32初學(xué)入門筆記(2):STM32CubeMX配置STM32輸出可調(diào)PWM方波的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!