接上篇笔记:http://bbs.mydigit.cn/read.php?tid=2176066 我们的最终目的是控制IO端口发出PWM信号,即脉冲宽度可自由变化的方波信号,本篇就来实现它。 先用PWM的方法替代上篇的中断方法:#include<intrinsics.h>#include<iostm8s103f3.h>int main(){ TIM2_PSCR =11; // Timer2的预分频2048 TIM2_ARRH =0x03; // Timer2的自动重载寄存器高位 TIM2_ARRL =0xD0; // Timer2的自动重载寄存器低位 两个合起来是 976 TIM2_CCR2H =0x2; // 低电平保持时间 TIM2_CCR2L =0x58; TIM2_CCER1_CC2P = 1; // lowlevel TIM2_CCER1_CC2E = 1; // OC2 TIM2_CCMR2_OC2PE = 1; // 通道2预装载使能 TIM2_CCMR2_OC2M = 0x6; //110 PWM模式1 TIM2_CR1_CEN =1; // 启动定时器2 while(1) { __no_operation(); // 空操作可以用 asm("nop"); 代替 }} 程序解释: 这里我们使用了定时器2,没有使用定时器1,因为之后还有其他用处。TIM2_PSCR是定时器2的预分频寄存器,但与定时器1不同,它只使用了4位,分频数只有16种,实际分频值是2的幂次,如11是2的11次幂,分频值就是2048。设置TIM2_ARR(Timer2一周期计数次数)为0x3D0(十进制的976),与2048相乘接近2000000,将2MHz按此数进行计数,得到一个计数周期是1秒。 TIM2_CCMR2_OC2M = 0x6; 这一句将Timer2的通道2设置成PWM模式1。 TIM2_CCMR2_OC2PE = 1; 这一句使TIM2的通道2能连续工作。 因为PWM功能使用了定时器的输入/捕获功能,所以设置: TIM2_CCER1_CC2P = 1; TIM2的通道2低电平有效, TIM2_CCER1_CC2E = 1; TIM2的通道2作为输出,TIM2产生的PWM脉冲将从TIM2的通道2输出。 TIM2_CCR2 决定了PWM脉冲的占空比,因为TIM2_ARR的值为976,所以它的取值可以从0到976(大于也可以)。这里取了整个周期的2/3左右。 最后用TIM2_CR1_CEN= 1 启动定时器2。
看一下上图,PD3复用为TIM2_CH2,正是Timer2的第二通道,所以定时器2启动后,D3口2/3时间保持低电平,LED亮,1/3时间为高电平,LED灭。由于核心板的LED在B5上,我们用导线将D3与B5联通,B5端口未进行设置,所以它保持输入的高阻状态,等于由D3接管了LED。
PWM信号周期是1秒,控制了LED的闪烁,我们的目的是用PWM实现LED亮度控制,为此可提高PWM频率,到1000Hz,人眼就观察不到闪烁,调整占空比就改变了亮度。 修改一下前三句: TIM2_PSCR =0; // Timer2的预分频1 TIM2_ARRH =0x07; // Timer2的自动重载寄存器高位 TIM2_ARRL =0xD0; // Timer2的自动重载寄存器低位 两个合起来是 2000 TIM2不再进行预分频,计数周期改为2000次,2MHz的主机脉冲导致定时器计数频率为1KHz。 运行后LED连续发光,亮度比之前降低了。 试改变TIM2_CCR2的值,调整占空比:
LED亮度随着改变。 更进一步,做一个亮度渐亮渐暗的呼吸灯。代码:
#include<intrinsics.h>#include<iostm8s103f3.h>int level = 0; int inc = 1;int main(){ TIM2_PSCR = 0; // Timer2的预分频1 TIM2_ARRH = 0x07; // Timer2的自动重载寄存器高位 TIM2_ARRL = 0xD0; // Timer2的自动重载寄存器低位 两个合起来是 2000 TIM2_CCR2H = 0x2; // 低电平保持时间 TIM2_CCR2L = 0x58; TIM2_CCER1_CC2P = 1; // low level TIM2_CCER1_CC2E = 1; // OC2 TIM2_CCMR2_OC2PE = 1; // 通道2预装载使能 TIM2_CCMR2_OC2M = 0x6; //110 PWM模式1 TIM2_CR1_CEN = 1; // 启动定时器2 TIM1_PSCRH = 0x03; // Timer1的预分频寄存器值高位, 与低位合起来值是999,即Timer1的输入时钟分频1000 TIM1_PSCRL = 0xE7; // Timer1第预分频寄存器值低位 TIM1_ARRH = 0x0; // Timer1的自动重载寄存器高位 TIM1_ARRL = 0x20; // Timer1的自动重载寄存器低位 TIM1_IER_UIE = 1; // 设置Timer1中断允许 TIM1_CR1_ARPE = 1; // 设置Timer1为自动重载模式 TIM1_CR1_CEN = 1; // 设置Timer1启动计时 __enable_interrupt(); // 开系统中断 可用 asm("rim"); 代替 while(1) { __no_operation(); // 空操作 可以用 asm("nop"); 代替 }} #pragma vector =TIM1_OVR_UIF_vector //设置Timer1的中断向量地址,其值为 0xD __interrupt voidTIM1_IRQ(void) // Timer1 的中断程序{ TIM1_SR1_UIF = 0; level += inc; if(level >= 100) inc = -1; else if(level <= 0) inc = 1; int CCR2 = 20 * level; // 0 - 2000, 亮度变化100级 TIM2_CCR2H = (CCR2 >> 8); // 低电平保持时间 TIM2_CCR2L = CCR2 & 0xFF;} 与上篇类似,我们又使用了定时器1,每隔一定时间产生中断,全局变量level决定亮度级别,从0到100来回变化。在中断处理过程中,当level大于0时inc为+1,level +=inc使level每次增量1,达到100后,inc = -1,level +=inc使level每次减1,减到0时inc = 1。局部16位变量CCR2 =20 * level,其值变化范围为0-2000,符合定时器2的计数周期范围。 语句: TIM2_CCR2H = (CCR2 >> 8); // CCR2右移8位,取出CCR2高8位 TIM2_CCR2L = CCR2 & 0xFF; // 取CCR2低8位 将CCR2拆分为高、低8位分别赋值给占空比控制寄存器TIM2_CCR2的高8位和低8位。 运行后LED变成了呼吸灯。 STM8的定时器比较复杂,我只是使用了极少的功能,也是反复把手册的这部分前后看了若干遍,第一遍看的时候不明白的概念比较多,看下来一头雾水,不过初学就这样,不能轻易放弃,看一遍再通过实践试错就能弄懂一两个概念,再回头看难度就减少一些,又能弄懂一两个概念,如此反复迭代多次低手就能变成高手了。 待续…下一篇:http://bbs.mydigit.cn/read.php?tid=2177114&ds=1[ 此帖被fox69在2017-07-29 16:16重新编辑 ]