STM32筆記(22):PWM基本概念與呼吸燈(下)


[PWM 設定步驟]

要編寫 PWM 程式,可按照如下步驟進行設定:
(1) 設定 GPIO 及 Timer 的 RCC 時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);                 //使能TIM3時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);	//使能 GPIOB 時鐘和 AFIO 複用時鐘
(2) GPIO 設為復用推輓輸出,並將 TIM3 重映射
定義一個 GPIO_InitTypeDef 類型的結構體
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);   //TIM3部分重映射 TIM3_CH2->PB5

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;          
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;      //複用推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;    //配置輸出速率
GPIO_Init(GPIOB, &GPIO_InitStructure);         	//初始化GPIOB
相關 GPIO 相關設定說明,請參考:STM32筆記(17):按鍵控制-使用 GPIO 讀取

(3) 設定定時器:設定重裝載值 ARR 與分頻係數 PSC 等
定義一個 TIM_TimeBaseInitTypeDef 類型的結構體
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;    //定義TIMx定時器結構體

TIM_TimeBaseStructure.TIM_Period = arr;            //設置自動重裝載暫存器週期的值 arr = value - 1
TIM_TimeBaseStructure.TIM_Prescaler = psc;          //設置預分頻值 psc = value - 1
TIM_TimeBaseStructure.TIM_ClockDivision = 0; 		//設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;		//TIM向上計數模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);	//初始化 TIM3 PWM 週期
(4) 初始化外設 OC2 比較參數,並進行預載暫存器
//初始化TIM3 Channel2 PWM模式  
TIM_OCInitTypeDef TIM_OCInitStructure;		//定義結構體

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;		//選擇定時器模式:TIM脈衝寬度調製模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//Enable 比較輸出
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;	 //輸出極性:TIM輸出比較極性高
TIM_OCInitStructure.TIM_Pulse = 0;

TIM_OC2Init(TIM3, &TIM_OCInitStructure);	//初始化外設 TIM3 OC2 
TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);   //啟動 TIM3 在 CCR2 預載暫存器
(5) 啟用定時器
TIM_Cmd(TIM3, ENABLE); 		//啟動定时器 TIM3
(6)在主函數中不斷改變比較值 CCRx,達到不同的占空比效果
TIM_SetCompare2(TIM3,pwmValue);   //通道2, 改變比較值CCRx
依照上述幾個步驟,就可完成定 PWM 的控制,以下就用一個定時器進行 PWM 的實做,透過改變 CCR 值讓 LED 出現明暗的變化,好像在呼吸一樣,並將 CCR 比較值顯示在 OLED 上。

[材料]

  • STM32F103C8T6 主板 x 1
  • OLED SSD1306 顯示器 x 1
  • STLINK V2 模擬下載器 x 1
  • LED x 1
  • 麵包板 x 1
  • 連接線 x N 條

[接線與電路圖]

LED 的負極接地,另一腳接在 STM32F103x PB5,與 SSD1306 連接的方式如下:
STM32F103xSSD1306 OLED
3.3vVDD
GNDGND
B8SCK/SCL
B9SDA


[程式]

主程式一開始先對 OLED 進行初始化,進入迴圈循環後就改變 CCR 值,等中斷產生,主程式 main.c 如下:
#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "bmp.h"
#include "pwm.h"

//主函數	
int main(void)
{
    u16 pwmValue = 1;
    u8 dir = 1;
	delay_init();	    	 //延時函數初始化	  
	
	OLED_Init();		 //初始化OLED  
	OLED_Clear(0);           //清除螢幕
	OLED_ShowString(2, 2, "Count:",16);	
	
    PWM_Init(99, 7199);
	
    while(1)
    {
		OLED_ShowNum(60, 2, pwmValue,3, 16); 		
        delay_ms(30);		
        if(dir) pwmValue++;  //從暗到亮
		else pwmValue--;     //從亮到暗
		
		if(pwmValue >= 100) dir = 0;
        if(pwmValue <= 1) dir = 1;

		TIM_SetCompare2(TIM3,pwmValue);   //通道2, 改變比較值CCRx
    }
}

pwm.c 程式如下:
#include "pwm.h"

void PWM_Init(u16 arr,u16 psc)
{
    GPIO_InitTypeDef GPIO_InitStructure;              //定義GPIO結構體
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;    //定義TIMx定時器結構體
    TIM_OCInitTypeDef TIM_OCInitStructure;            //定義定時器脈寬調製結構體
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);                     //使能TIM3時鐘
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);//使能GPIOB時鐘和AFIO複用時鐘
    
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);    //TIM3部分重映射 TIM3_CH2->PB5
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;              //TIM_CH2
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //複用推輓輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;      //配置輸出速率
    GPIO_Init(GPIOB,&GPIO_InitStructure);                  //初始化GPIOB
    
    TIM_TimeBaseStructure.TIM_Period = arr;                //設置自動重裝載暫存器週期的值 arr=value-1
    TIM_TimeBaseStructure.TIM_Prescaler = psc;             //設置預分頻值 psc=value-1
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;           //設置時鐘分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;    //TIM向上計數模式
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);         //初始化TIMx時間基數
    
    //初始化TIM3 Channel2 PWM模式     
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;             //選擇定時器模式:TIM脈衝寬度調製模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能比較輸出
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;     //輸出極性:TIM輸出比較極性高
    TIM_OCInitStructure.TIM_Pulse = 0;
    TIM_OC2Init(TIM3,&TIM_OCInitStructure);              //根據T指定的參數初始化外設TIM3 OC2

    TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);      //使能TIM3在CCR2上的預裝載暫存器
    TIM_Cmd(TIM3, ENABLE);                       	//使能TIM3
}

pwm.h 程式如下:
#ifndef __PWM_H_
#define __PWM_H_
#include "stm32f10x.h"
 
void PWM_Init(u16 arr,u16 psc);
 
#endif

完整的程式請參考 Github:8.Breathing light with PWM

[結果]

因開燈攝影呼吸的效果不明顯,將燈光調暗後,呈現以下的影像。

[參考資料]

2 留言

  1. 您好,請問可以教學如何使用stm32做PI控制嗎

    回覆刪除
    回覆
    1. 請問, 所謂的控制PI,是讓PI接周邊裝置,如馬達等,讓STM32遠端控制 PI 啟動馬達嗎?或請描述一下情境?

      刪除

張貼留言

較新的 較舊