
---------------------------------------------------
IDE: TI CCS 20.3.0
--------------------------------------------------PWM訊號用途蠻廣的 例如 調光 馬達控制
下面程式提供
一個標準的PWM功能(P2.0) 使用500Hz
跟一個用GPIO模擬PWM的功能(P1.0) 使用1.5kHz
主要是PWM功能要使用Timer
同一條Timer 需要一樣的頻率
若Timer與腳位對不起來
就可能要改慮使用GPIO加軟體的方式
去模擬PWM訊號
當然若是夠用就不要用模擬的
#include <msp430.h>
#include <stdint.h>
//Define++++++++++++++++++++++++++++++++++
//PWM Part
#define SMCLK_HZ 1000000UL
#define PWM_FREQ 1450UL //1.5kHz
#define PERIOD_TCK (SMCLK_HZ / PWM_FREQ)
#define PWM_BIT BIT0
//變數++++++++++++++++++++++++++++++++++
uint8_t i = 0;
//PWM Part 變數-----------------------------------------------------------------
//unsigned long MianFreq1=750;//1KHz
//unsigned long MianFreq1=1500;//700Hz
//unsigned long MianFreq1=1900;//550Hz
unsigned long MianFreq1=2100;//500Hz P2.0用
static volatile uint16_t duty_ticks_p10 = PERIOD_TCK/2; // P1.0用
//函式++++++++++++++++++++++++++++++++++
//延遲函式
void delay_ms(uint16_t ms)
{
while (ms--) __delay_cycles(1000); // 假設 1 MHz 時鐘
}
//PWMPart----------------------------------------------------------------------
// ===== PWM 初始化(P2.0)=====
void pwm_p20_init(void)
{
// 設定 P2.0 為 Timer 輸出 (PWM)
P2DIR |= BIT0; // 設置 P2.0 為輸出
P2SEL0 |= BIT0; // 選擇 P2.0 的 Timer 功能
//P2.0
// 設置 Timer_B1 以產生 PWM,並配置 CCR1
TB1CCR0 = MianFreq1;
// P2.0 使用 TB1.1 (CCR1) 輸出 PWM
TB1CCTL1 = OUTMOD_7; // 設定 CCR1 為 Reset/Set 模式
//TB1CCR1 = 100;
TB1CTL = TBSSEL__SMCLK | MC__UP | TBCLR; // 使用 SMCLK,Up 模式運行,清除計數器
}
// ===== PWM 初始化(P1.0 軟體 PWM)=====
void pwm_p10_init(void)
{
P1DIR |= PWM_BIT;
P1OUT &= ~PWM_BIT;
TB0CTL = TBSSEL__SMCLK | MC__UP | TBCLR;
TB0CCR0 = PERIOD_TCK - 1;
TB0CCTL0 = CCIE; // CCR0 中斷:週期開始
TB0CCR1 = duty_ticks_p10;
TB0CCTL1 = CCIE; // CCR1 中斷:duty 結束
}
void pwm_init(void)
{
pwm_p10_init();//1.5K Hz
pwm_p20_init();//500 Hz
}
// Timer ISR
// CCR0: 每個週期開始 → 拉高
#pragma vector = TIMER0_B0_VECTOR
__interrupt void TIMER0_B0_ISR(void)
{
if (duty_ticks_p10 > 0)
P1OUT |= PWM_BIT;
else
P1OUT &= ~PWM_BIT;
TB0CCTL0 &= ~CCIFG; // ★ 一定要清除 CCR0 中斷旗標!
}
// CCR1: duty 到點 → 拉低
#pragma vector = TIMER0_B1_VECTOR
__interrupt void TIMER0_B1_ISR(void)
{
switch (__even_in_range(TB0IV, TB0IV_TBIFG)) {
case TB0IV_TBCCR1:
if (duty_ticks_p10 < PERIOD_TCK) P1OUT &= ~PWM_BIT;
break;
default: break;
}
}
// ===== 設定 PWM duty(百分比 0~100)=====
// 設定 P2.0 (TB1.1) PWM Duty (0~100%)
void pwm_p20_set_duty(uint8_t percent)
{
if (percent >= 100) {
TB1CCR1 = TB1CCR0; // 100% duty → 永遠高
} else if (percent == 0) {
TB1CCR1 = 0; // 0% duty → 永遠低
} else {
// 四捨五入計算 TB1CCR1
TB1CCR1 = (uint16_t)(((uint32_t)(TB1CCR0 + 1) * percent + 50) / 100);
}
}
void pwm_p10_set_duty(uint8_t percent)
{
if (percent >= 100) {
duty_ticks_p10 = PERIOD_TCK;
}else{
duty_ticks_p10 = (uint16_t)((PERIOD_TCK * (uint32_t)percent + 50) / 100);
}
TB0CCR1 = (duty_ticks_p10 == 0) ? 1 : duty_ticks_p10;
}
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
PM5CTL0 &= ~LOCKLPM5; // 解除高阻態,讓 GPIO 可用(即使本例沒用到腳位,建議保留)
//PWM Init
pwm_init();
__enable_interrupt(); // 開啟中斷
//pwm_p10_set_duty(100);
while(1){
//漸亮
for (i = 0; i <= 100; i++) {
pwm_p10_set_duty(i);
pwm_p20_set_duty(i);
delay_ms(10); // 改這裡控制呼吸速度
}
// 漸暗
for (i = 100; i > 0; i--) {
pwm_p10_set_duty(i);
pwm_p20_set_duty(i);
delay_ms(10);
}
}
}
PWM duty越低 在開發版上看到的亮度應該會越暗
有不清楚地可以在留言區留言
大家可以來討論
之後也會用 YT影片(@GGEggProgramming)的方式來說明內容及驗證
================
有其他MCU或是功能想知道怎麼開發
也可以留言讓G蛋知道
G蛋若能力能即盡量幫忙🤣














