小編又回顧了一下野火的高級定時器的視頻,然后就想實作一個串口控制實作可調PWM波的實作,整了兩天終于搞明白咋回事了,我太菜了,下面不多說直接上實驗
工程是在野火的源檔案的基礎上修改的,不要噴昂
工程里的User檔案包括以下內容:

其中的GeneralTim檔案夾包含:bsp_GeneralTim.c和bsp_GeneralTim.h
Usart檔案夾包含:bsp_usart.c和bsp_usart.h
好了,直接上代碼:
main.c
// TIM—通用定時器(TIM3)-1路(通道3)可調PWM輸出應用
#include "stm32f10x.h"
#include "bsp_GeneralTim.h"
#include "bsp_usart.h"
uint16_t GENERAL_TIM_Period=9;//主函式中的周期的全域變數,周期初始化給個值9(TIM內部暫存器函式輸出比較暫存器值是5)
uint16_t CCR_Valeur_Fix=5; //給GENERAL_TIM_Mode_Config中的形參CCR_Valeur_Fix設定一個固定值
static void Show_Message(void);
/**
* @brief 主函式
* @param 無
* @retval 無
*/
int main(void)
{
//定義變數,獲取串口發來的資料
char ch;
/* 定時器初始化 ,先給一個固定的脈沖*/
GENERAL_TIM_GPIO_Config();
GENERAL_TIM_Mode_Config(GENERAL_TIM_Period,CCR_Valeur_Fix);
/*串口初始化*/
USART_Config();
Show_Message();
while(1)
{
ch=getchar();
switch(ch){
case '0':
CCR_Valeur_Fix--; //占空比變小******
printf("\r\n 減速 \n");
break;
case '1':
CCR_Valeur_Fix++; //占空比變大*****
printf("\r\n 加速了 \n");
break;
}
if(((GENERAL_TIM_Period >= CCR_Valeur_Fix)&&(CCR_Valeur_Fix>=0))==1) { //控制占空比在0-1之間 *****
GENERAL_TIM_Mode_Config(GENERAL_TIM_Period,CCR_Valeur_Fix);
}else{printf("\r\n 請改變控制方式 \n");}
}
}
static void Show_Message(void)
{
printf("\r\n 這是一個通過串口通信指令控制A5電平的實驗 \n");
printf("使用 USART 引數為:%d 8-N-1 \n",DEBUG_USART_BAUDRATE);
printf("開發板接到指令后A5電平,指令對應如下:\n");
printf(" 0 ------ 加速 \n");
printf(" 1 ------ 減速 \n");
}
/*********************************************END OF FILE**********************/
總結:(周期常量GENERAL_TIM_Period)和CRR(占空比:輸出比較暫存器CCR_Valeur_Fix)的大小比較,因為占空比是不會大于1的,所以加了f陳述句,if陳述句用于限制占空比在0-1的范圍內,實質上就是限制ARR始終大于或等于CCR并且CCR大于或等于0控制操作才有效,
bsp_usart.h
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
#include <stdio.h>
/**
* 串口宏定義,不同的串口掛載的總線不一樣,移植時需要修改這幾個宏
*/
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引腳宏定義
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler
void USART_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);
#endif /* __USART_H */
bsp_usart.c
#include "bsp_usart.h"
/**
* @brief USART GPIO 配置,作業引數配置
* @param 無
* @retval 無
*/
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打開串口GPIO的時鐘
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打開串口外設的時鐘
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 將USART Tx的GPIO配置為推挽復用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 將USART Rx的GPIO配置為浮空輸入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的作業引數
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 針資料字長
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
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(DEBUG_USARTx, &USART_InitStructure);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
/***************** 發送一個字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
/* 發送一個位元組資料到USART */
USART_SendData(pUSARTx,ch);
/* 等待發送資料暫存器為空 */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
/***************** 發送字串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='\0');
/* 等待發送完成 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
{}
}
/***************** 發送一個16位數 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
uint8_t temp_h, temp_l;
/* 取出高八位 */
temp_h = (ch&0XFF00)>>8;
/* 取出低八位 */
temp_l = ch&0XFF;
/* 發送高八位 */
USART_SendData(pUSARTx,temp_h);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
/* 發送低八位 */
USART_SendData(pUSARTx,temp_l);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
///重定向c庫函式printf到串口,重定向后可使用printf函式
int fputc(int ch, FILE *f)
{
/* 發送一個位元組資料到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/* 等待發送完畢 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
}
///重定向c庫函式scanf到串口,重寫向后可使用scanf、getchar等函式
int fgetc(FILE *f)
{
/* 等待串口輸入資料 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_USARTx);
}
整個bsp_usart.c和bsp_usart.h檔案我是直接移植野火的程式來的,初始化了usart1串口,因為沒有涉及中斷服務程式,所以.c檔案里也沒有移植什么優先級配置函式
bsp_GeneralTim.h
#ifndef __BSP_GENERALTIME_H
#define __BSP_GENERALTIME_H
#include "stm32f10x.h"
/************通用定時器TIM引數定義,只限TIM2、3、4、5************/
// 當使用不同的定時器的時候,對應的GPIO是不一樣的,這點要注意
// 我們這里默認使用TIM3
#define GENERAL_TIM TIM3
#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK RCC_APB1Periph_TIM3
#define GENERAL_TIM_Prescaler 720 //72M/720=10KHz頻率
// TIM3 輸出比較通道3(B0)
#define GENERAL_TIM_CH3_GPIO_CLK RCC_APB2Periph_GPIOB
#define GENERAL_TIM_CH3_PORT GPIOB
#define GENERAL_TIM_CH3_PIN GPIO_Pin_0
/**************************函式宣告********************************/
//下面的這兩個函式即可實作通用定時器TIM3的通道3的PWM波可調
//初始化TIM3的GPIO外設
void GENERAL_TIM_GPIO_Config(void);
//TIM3通用定時器的內部暫存器設定(這個引數很重要,通過ARR周期不變,改變CCR高電平時間的方式實作PWM波的可調)
void GENERAL_TIM_Mode_Config(uint16_t GENERAL_TIM_Period,uint16_t CCR_Valeur_Fix);
#endif /* __BSP_GENERALTIME_H */
bsp_GeneralTim.c
#include "bsp_GeneralTim.h"
void GENERAL_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 輸出比較通道3 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH3_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStructure);
}
///*
// * 注意:TIM_TimeBaseInitTypeDef結構體里面有5個成員,TIM6和TIM7的暫存器里面只有
// * TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的時候只需初始化這兩個成員即可,
// * 另外三個成員是通用定時器和高級定時器才有.
// *-----------------------------------------------------------------------------
// *typedef struct
// *{ TIM_Prescaler 都有
// * TIM_CounterMode TIMx,x[6,7]沒有,其他都有
// * TIM_Period 都有
// * TIM_ClockDivision TIMx,x[6,7]沒有,其他都有
// * TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有
// *}TIM_TimeBaseInitTypeDef;
// *-----------------------------------------------------------------------------
// */
/* ---------------- PWM信號 周期和占空比的計算--------------- */
// ARR :自動重裝載暫存器的值
// CLK_cnt:計數器的時鐘,等于 Fck_int / (psc+1) = 72M/(psc+1)
// PWM 信號的周期 T = ARR * (1/CLK_cnt) = ARR*(PSC+1) / 72M
// 占空比P=CCR/(ARR+1)
void GENERAL_TIM_Mode_Config(uint16_t GENERAL_TIM_Period,uint16_t CCR_Valeur_Fix)
{
// 開啟定時器時鐘,即內部時鐘CK_INT=72M
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
/*--------------------時基結構體初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自動重裝載暫存器的值,累計TIM_Period+1個頻率后產生一個更新或者中斷
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period;
// 驅動CNT計數器的時鐘 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler;
// 時鐘分頻因子 ,配置死區時間時需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 計數器計數模式,設定為向上計數
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重復計數器的值,沒用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定時器
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
/*--------------------輸出比較結構體初始化-------------------*/
// CCR占空比配置,通過引數配置一個固定值
uint16_t CCR3_Val = CCR_Valeur_Fix;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 配置為PWM模式1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
// 輸出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 輸出通道電平極性配置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// 輸出比較通道 3
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 使能計數器
TIM_Cmd(GENERAL_TIM, ENABLE);
}
/*********************************************END OF FILE**********************/
bsp_GeneralTim.c和bsp_GeneralTim.h檔案我是在野火的通用暫存器輸出四路不同PWM波的原碼上修改的;要修改的步驟如下:
1、bsp_GeneralTim.h檔案中僅保留“TIM3 輸出比較通道3”的通道宏定義,并洗掉了周期的宏定義GENERAL_TIM_Period
2、bsp_GeneralTim.c檔案中將原本的無引數函式static void GENERAL_TIM_Mode_Config(void),改變為有引數函式void GENERAL_TIM_Mode_Config(uint16_t GENERAL_TIM_Period,uint16_t CCR_Valeur_Fix),前一個引數定周期長度,后一個引數定高電平長度,其次不使用void GENERAL_TIM_Init(void);因為要實作PWM的可調的話,對應GPIO和定時器的初始化必須分開,通過在主函式whlie不斷回圈查詢串口發來的信號并利用switch判斷調整變數uint16_t CCR_Valeur_Fix的值,再呼叫void GENERAL_TIM_Mode_Config(uint16_t GENERAL_TIM_Period,uint16_t CCR_Valeur_Fix)使得引腳的PWM輸出發生改變,
實驗圖
1、程式剛下載后,PWM輸出的情況

2、串口發送幾次‘0’信號的情況
3、串口發送幾次‘1’的情況

4、CCR的值小于0或CCR好ARR的比值大于1的情況


最后敲黑板
記住這幾個暫存器很重要
計數器 CNT
自動多載暫存器 ARR
CCR輸出比較暫存器
*****CNT從 0開始計數,當 CNT<CCR的 值時,OCxREF 為有效的高電平,于此同時,比較中斷暫存器 CCxIF 置位,當 CCR=<CNT<=ARR 時,OCxREF 為無效的低電平,然后 CNT 又從 0 開始計數并生成計數 器上溢事件,以此回圈往復,
重點:這個實驗的重點在于bsp_GeneralTim.c中的
void GENERAL_TIM_Mode_Config(uint16_t GENERAL_TIM_Period,uint16_t CCR_Valeur_Fix){} 函式;在這個實驗中我們讓引數uint16_t GENERAL_TIM_Period不變,在main.c中給了個常量,將引數uint16_t CCR_Valeur_Fix作為變數,這樣我們就可以得到一個頻率不變(本實驗為10KHz),而占空比改變的PWM波,
如果我們使得uint16_t CCR_Valeur_Fix作為常量,而uint16_t GENERAL_TIM_Period作為變數,則可以實作得到一個頻率變化的可調PWM波,有興趣的同學可以試試,
希望對大家的學習有幫助,嘻嘻,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/157479.html
標籤:其他
