電子產(chǎn)業(yè)一站式賦能平臺(tái)

PCB聯(lián)盟網(wǎng)

搜索
查看: 1712|回復(fù): 0
收起左側(cè)

[STM32] 無法控制輸出脈沖?試試雙定時(shí)器的強(qiáng)強(qiáng)聯(lián)手

[復(fù)制鏈接]

633

主題

1927

帖子

8108

積分

聯(lián)盟顧問

Rank: 3Rank: 3

積分
8108
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2022-7-16 07:35:18 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
#申請(qǐng)?jiān)瓌?chuàng)# @21小跑堂




如圖,最近在逛論壇,看到幾個(gè)帖子都在咨詢?nèi)绾慰刂茊纹瑱C(jī)輸出固定的數(shù)量的PWM脈沖,用于控制電機(jī)的轉(zhuǎn)停,剛好前兩天本人也需要該功能做測(cè)試,我是輸出PWM給伺服電機(jī)驅(qū)動(dòng)器,驅(qū)動(dòng)器以位置模式工作,收到脈沖就控制電機(jī)轉(zhuǎn)動(dòng),如果需要精確控制電機(jī)轉(zhuǎn)過的角度,就需要給驅(qū)動(dòng)器輸入固定數(shù)量的脈沖。于是我便用STM32F031的雙定時(shí)器實(shí)現(xiàn)了該功能,下文便詳細(xì)描述。
我在進(jìn)行代碼編譯之前也在網(wǎng)絡(luò)上搜索過相應(yīng)的方法,總結(jié)起來一共五個(gè)方法:
1、單脈沖法,需要一個(gè)脈沖中斷一次,中斷次數(shù)多,影響效率
2、一個(gè)定時(shí)器輸出PWM,另一定時(shí)器使用輸入捕獲進(jìn)行中斷計(jì)數(shù),與方法1一樣,同樣需要頻繁的中斷
3、用主從定時(shí)器門控方式,比較繁瑣
4、用一個(gè)定時(shí)器(從)作為另一個(gè)定時(shí)器(主)的外部時(shí)鐘觸發(fā)源
5、高級(jí)定時(shí)器T1、T8的重復(fù)計(jì)數(shù)方式,RCR計(jì)數(shù)中斷,看手冊(cè)好像這種方式最簡(jiǎn)單,能滿足一部分人要求,缺點(diǎn)是寄存器只有8位,最多實(shí)現(xiàn)255個(gè)脈沖計(jì)數(shù)輸出。
我在最初時(shí)使用了第2和方法,該方法對(duì)于我來說你叫簡(jiǎn)單,后來在寫這篇文章時(shí)選擇了第4個(gè)方法,總結(jié)起來還是4比較靠譜,但是這里的第2方法也描述一下,便于大家選擇。
方法2:
因?yàn)闂l件限制,干脆說為了省事,我在原來用于其他功能的板子上進(jìn)行測(cè)試,因?yàn)橹婚_放了PB3和PB4,所以這里只好用TIM2和TIM3進(jìn)行測(cè)試。
TIM2用于產(chǎn)生PWM脈沖輸出,在輸出給驅(qū)動(dòng)器的同時(shí)將該脈沖也接到PB4,也就是TIM3的輸入口,這樣TIM3也能接收到TIM2發(fā)出的脈沖,TIM3只需要配置為輸入捕獲,并開啟中斷,便可以在每次脈沖到來進(jìn)入中斷,在TIM3的中斷中去計(jì)數(shù),達(dá)到需要的脈沖數(shù)便關(guān)閉TIM2便可。
首先依舊是初始化端口:
先貼一下time.h文件:
/* 定義防止遞歸包含 ----------------------------------------------------------*/
#ifndef _TIMER_H
#define _TIMER_H
/* 包含的頭文件 --------------------------------------------------------------*/
#include "stm32f0xx.h"
/* 宏定義 --------------------------------------------------------------------*/
#define TIM6_COUNTER_CLOCK 1000000 //計(jì)數(shù)時(shí)鐘(1M次/秒)
//預(yù)分頻值
#define TIM6_PRESCALER_VALUE (SystemcoreClock/TIM6_COUNTER_CLOCK - 1)
#define TIM6_PERIOD_TIMING (10 - 1) //定時(shí)周期(相對(duì)于計(jì)數(shù)時(shí)鐘:1周期 = 1計(jì)數(shù)時(shí)鐘)
#define TIM2_COUNTER_CLOCK 24000000 //計(jì)數(shù)時(shí)鐘(24M次/秒)
//預(yù)分頻值
#define TIM2_PRESCALER_VALUE (SystemCoreClock/TIM2_COUNTER_CLOCK - 1)
/* 函數(shù)申明 ------------------------------------------------------------------*/
void Systick_Init(void);
void Delay_ms(__IO uint32_t nTime);
void TimingDelay_Decrement(void);
void Delay(uint32_t temp);
void delay_us(uint32_t nus);
void delay_init();
void TIMER_Initializes(void);
void TIMDelay_N10us(uint16_t Times);
void TIMDelay_Nms(uint16_t Times);
void TIMDelay_Ns(uint16_t Times);
void TIMER_PWM_GPIO_Configuration(void);
void TIM2_CH2_PWM(uint32_t Freq, uint16_t Dutycycle);
void TIMER_IC_Configuration(void);
#endif /* _TIMER_H */
復(fù)制代碼
因?yàn)槲业臅r(shí)鐘初始化是單獨(dú)定義的,所以這里未進(jìn)行時(shí)鐘的初始化,在參考的我的代碼時(shí)需注意:
void TIMER_PWM_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //TIM2引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //復(fù)用模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //高速輸出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推完輸出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_2); //復(fù)用配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //TIM3引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //復(fù)用模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //高速輸出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推完輸出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //無上下拉(浮空)
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_1);
}
復(fù)制代碼
配置定時(shí)器2,TIM2作為PWM的脈沖輸出:
/************************************************
函數(shù)名稱 : TIM2_CH2_PWM
功 能 : 定時(shí)器2通道2輸出PWM
參 數(shù) : Freq -------- 頻率
Dutycycle --- 占空比
返 回 值 : 無
作 者 : 吶咯密密
*************************************************/
void TIM2_CH2_PWM(uint32_t Freq, uint16_t Dutycycle)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint16_t tim2_period;
uint16_t tim2_pulse;
tim2_period = (uint16_t)(TIM2_COUNTER_CLOCK/Freq - 1); //計(jì)算出計(jì)數(shù)周期(決定輸出的頻率)
tim2_pulse = (tim2_period + 1)*Dutycycle / 100; //計(jì)算出脈寬值(決定PWM占空比)
/* TIM2時(shí)基單元配置 */
TIM_TimeBaseStructure.TIM_Prescaler = TIM2_PRESCALER_VALUE; //預(yù)分頻值
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上計(jì)數(shù)模式
TIM_TimeBaseStructure.TIM_Period = tim2_period; //定時(shí)周期(自動(dòng)從裝載寄存器ARR的值)
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時(shí)鐘分頻因子
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM2通道2:PWM1模式配置 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //輸出PWM1模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能輸出
TIM_OCInitStructure.TIM_Pulse = tim2_pulse; //脈寬值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE); //初始化PWM。
}
復(fù)制代碼
配置定時(shí)器3,作為捕獲輸入:
void TIMER_IC_Configuration(void)
{
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = 1 - 1; //1分頻(與捕獲分頻相同)
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上計(jì)數(shù)模式
TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF; //定時(shí)周期(自動(dòng)從裝載寄存器ARR的值)
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時(shí)鐘分頻因子
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //通道1
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; //捕獲極性
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //捕獲選擇
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕獲分頻
TIM_ICInitStructure.TIM_ICFilter = 0; //捕獲濾波
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM3->SR = (uint16_t)~TIM_IT_CC1; //清除中斷標(biāo)志
TIM_Cmd(TIM3, ENABLE); //使能TIM3
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //使能中斷
}
復(fù)制代碼
關(guān)于定時(shí)器的通道要根據(jù)手冊(cè)定義來確定,我的只適配我的硬件。
這里需要著重說一下預(yù)分頻TIM_Prescaler的值和捕獲分頻TIM_ICPrescaler的值要對(duì)應(yīng),在上面的代碼中這兩個(gè)值均為1,效果就是每來一個(gè)脈沖就會(huì)進(jìn)一次中斷。我們只需在中斷里進(jìn)行計(jì)數(shù),想要幾個(gè)脈沖就進(jìn)中斷幾次,達(dá)到需要的脈沖數(shù)就關(guān)閉TIM2。如下所示:
配置中斷:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //IRQ通道:定時(shí)器2
NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
復(fù)制代碼void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);//先清空中斷標(biāo)志位,以備下次使用。
capture++;
if(capture==16)
{
/*每16個(gè)脈沖轉(zhuǎn)動(dòng)電機(jī)一次*/
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);
TIM_ARRPreloadConfig(TIM2, DISABLE);
TIM_Cmd(TIM2, DISABLE);
TIM_Cmd(TIM3, DISABLE); //失能TIM2
TIM_ITConfig(TIM3, TIM_IT_CC1, DISABLE); //失能中斷
capture=0;
delay_us(5000);
TIM_Cmd(TIM3, ENABLE); //失能TIM2
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //失能中斷
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
}
}
/*
復(fù)制代碼
在TIM3的中斷函數(shù)中,我們定義一個(gè)變量capture,每次進(jìn)入中斷該值會(huì)加一,進(jìn)入16次中斷,也就是有16個(gè)脈沖輸入就會(huì)滿足條件進(jìn)入if()函數(shù),關(guān)閉TIM2和TIM3,延時(shí)5000us后再打開這兩個(gè)定時(shí)器,如此循環(huán)。可從示波器看現(xiàn)象:





現(xiàn)在我們已經(jīng)完成了對(duì)TIM2的輸出固定個(gè)數(shù)脈沖的試驗(yàn),但是這種方式每個(gè)脈沖都進(jìn)一次中斷太麻煩,于是可以修改預(yù)分頻TIM_Prescaler的值為8-1,和捕獲分頻TIM_ICPrescaler的值為TIM_ICPSC_DIV8,便可8個(gè)脈沖進(jìn)一次中斷。




此時(shí)也將中斷函數(shù)里的判斷條件改為1,進(jìn)一次中斷便會(huì)關(guān)閉定時(shí)器,我們接上示波器看看現(xiàn)象:




通過示波器我們可以看到,雖然只進(jìn)了一次中斷,但是我們卻輸出8個(gè)脈沖,以此可減少進(jìn)入中斷的次數(shù)。至此,通過TIM3的輸入捕獲控制PWM脈沖數(shù)的試驗(yàn)就完成了。
方法4:
方法4是利用主從定時(shí)器進(jìn)行脈寬調(diào)制,不占用主時(shí)鐘,在代碼時(shí)間要求苛刻和多電機(jī)控制時(shí)非常實(shí)用,可以精準(zhǔn)控制。
GPIO的初始化和上文保持不變,僅改變TIM的配置:
TIM2設(shè)置為主模式
/***********************TIM2初始化函數(shù)*************************
****參數(shù):****************************************************/
/******u32 Cycle用于設(shè)定計(jì)數(shù)頻率(計(jì)算公式:Cycle=1Mhz/目標(biāo)頻率)**
****返回值:**************************************************/
/******無*****************************************************/
void TIM2_config(uint32_t Cycle)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseStructure.TIM_Period = Cycle-1; //使用Cycle來控制頻率(f=48/(47+1)/Cycle) 當(dāng)Cycle為100時(shí)脈沖頻率為10KHZ
TIM_TimeBaseStructure.TIM_Prescaler =47; //設(shè)置用來作為TIMx時(shí)鐘頻率除數(shù)的預(yù)分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設(shè)置時(shí)鐘分割:TDTS= Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計(jì)數(shù)模式
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //重復(fù)計(jì)數(shù),一定要=0!!!
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時(shí)器模式:TIM脈沖寬度調(diào)制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_Pulse = Cycle/2-1; //設(shè)置待裝入捕獲寄存器的脈沖值(占空比:默認(rèn)50%,這可也可以調(diào)節(jié)如果需要的話將它作為一個(gè)參數(shù)傳入即可)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性
TIM_OC2Init(TIM2, &TIM_OCInitStructure); //使能通道2
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable); //設(shè)置為主從模式
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); //選擇定時(shí)器2的觸發(fā)方式(使用更新事件作為觸發(fā)輸出)
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能通道2預(yù)裝載寄存器
TIM_ARRPreloadConfig(TIM2, ENABLE); //使能TIM2在ARR上的預(yù)裝載寄存器
}
復(fù)制代碼
TIM3設(shè)置為從模式:
/***********************TIM3初始化函數(shù)*************************/
/****參數(shù):****************************************************/
/******u32 PulseNum用于設(shè)定脈沖數(shù)量****************************/
/****返回值:*************************************************/
/******無*****************************************************/
void TIM3_config(uint32_t PulseNum)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseStructure.TIM_Period = PulseNum-1; //設(shè)置自動(dòng)重裝載周期值
TIM_TimeBaseStructure.TIM_Prescaler =0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1);
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_External1 );// 等同 TIM3->SMCR|=0x07 //設(shè)置從模式寄存器
TIM_ITConfig(TIM3,TIM_IT_Update,DISABLE);
}
復(fù)制代碼
這里的TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1);是設(shè)置為內(nèi)部觸發(fā),參數(shù)由手冊(cè)進(jìn)行獲。




/************************脈沖輸出函數(shù)**************************/
/****參數(shù):****************************************************/
/******u32 Cycle用于設(shè)定計(jì)數(shù)頻率(計(jì)算公式:Cycle=1Mhz/目標(biāo)頻率)/
/******u32 PulseNum用于設(shè)定輸出脈沖的數(shù)量(單位:個(gè))************/
/****返回值:**************************************************/
/******無*****************************************************/
void Pulse_output(uint32_t Cycle,uint32_t PulseNum)
{
TIM3_config(PulseNum); //設(shè)置脈沖數(shù)量
TIM_Cmd(TIM3, ENABLE); //使能TIM3(從定時(shí)器)
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中斷標(biāo)志位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //使能更新中斷
TIM2_config(Cycle); //使能定時(shí)器2(主定時(shí)器)
TIM_Cmd(TIM2, ENABLE); //使能定時(shí)器2
// TIM_CtrlPWMOutputs(TIM2, ENABLE); //高級(jí)定時(shí)器一定要加上,主輸出使能
}
復(fù)制代碼void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //TIM_IT_Update
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); // 清除中斷標(biāo)志位
TIM_CtrlPWMOutputs(TIM2, DISABLE); //主輸出使能
TIM_Cmd(TIM2, DISABLE); //關(guān)閉定時(shí)器
TIM_Cmd(TIM3, DISABLE); //關(guān)閉定時(shí)器
TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE); //關(guān)閉TIM2更新中斷
}
}
復(fù)制代碼
當(dāng)TIM的CNT寄存器的值到達(dá)設(shè)定的Update值會(huì)觸發(fā)更新中斷,此時(shí)設(shè)定的脈沖數(shù)已輸出完畢,關(guān)閉TIM2和TIM3.
主函數(shù):








該代碼本人均已調(diào)通,原理部分過于繁雜,這里以本人能力可能無法解釋的清除,諸位可參考手冊(cè)或網(wǎng)絡(luò)獲取相關(guān)講解。
原文:https://bbs.21ic.com/icview-3127662-1-1.html

發(fā)表回復(fù)

本版積分規(guī)則


聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表