[原创].菜农M0助学板PWM呼吸灯小练(寄存器操作方式)
已发帖至:http://bbs.21ic.com/viewthread.php?tid=229192
关于菜农M0助学板(NUC120):http://bbs.21ic.com/iclist-78.html
使用串口0的接收中断,来控制闪灯类型。上电缺省不开灯。
发1,1Hz亮灭灯,占空比为50%;
发2,1.25Hz呼吸灯,占空比自己算;
发q,关灯
main.h
#ifndef __MAIN_H__ #define __MAIN_H__ #include <stdio.h> /********************************************************* * 系统寄存器映射及库头文件 *********************************************************/ #include "NUC1xx.h" // 系统寄存器映射 #include "DrvSYS.h" #include "DrvGPIO.h" #include "DrvUART.h" /********************************************************** * 自定义宏 **********************************************************/ #define APP_DEBUG #ifdef APP_DEBUG #define PRINT printf #else #define PRINT #endif /********************************************************** * 函数申明 **********************************************************/ extern char GetChar(void); extern void PFN_UART_CALLBACK(void); /********************************************************** * 宏及变量申明 **********************************************************/ typedef enum{NO=0, YES=!NO}bool; volatile bool g_tmr0_5ms = NO; volatile bool g_breathing_led = NO; // 呼吸灯标志 volatile uint8_t g_duty_cnt = 0; volatile bool g_duty_acc_dec = YES; #endif /* __MAIN_H__ */
main.c
#include "main.h" /********************************************************** * 系统上电初始化 **********************************************************/ void MAIN_INIT(void) { UNLOCKREG(); { /* 配置系统时钟 */ SYSCLK->PWRCON.XTL12M_EN = 1; // 设定12M外部晶振 DrvSYS_Delay(5000); // 等待时钟就绪 DrvSYS_SelectPLLSource(E_SYS_EXTERNAL_12M); // 选择12MHz为PLL输入 DrvSYS_Open(50000000); // 打开50MHz } { /* 配置串口 */ STR_UART_T param; DrvSYS_SelectIPClockSource(E_SYS_UART_CLKSRC, 0); //使能UART时钟 DrvGPIO_InitFunction(E_FUNC_UART0); // 复用功能引脚设置 param.u32BaudRate = 115200; // 波特率 param.u8cDataBits = DRVUART_DATABITS_8; // 数据位 param.u8cStopBits = DRVUART_STOPBITS_1; // 停止位 param.u8cParity = DRVUART_PARITY_NONE; // 校验位 param.u8cRxTriggerLevel = DRVUART_FIFO_1BYTES; // FIFO存储深度1字节 param.u8TimeOut = 0; // FIFO超时设定 DrvUART_Open(UART_PORT0, ¶m); // 串口开启、结构体整体赋值 // 串口的中断类型比较丰富,此处仅打开接收中断 DrvUART_EnableInt(UART_PORT0, DRVUART_RDAINT, (PFN_DRVUART_CALLBACK*)PFN_UART_CALLBACK); DrvUART_ClearIntFlag(UART_PORT0, DRVUART_RDAINT); } { /* 配置GPIO */ NVIC_DisableIRQ(GPAB_IRQn); NVIC_DisableIRQ(GPCDE_IRQn); DrvGPIO_Open(E_GPB, 10, E_IO_OUTPUT); // 蜂鸣器 DrvGPIO_ClrBit(E_GPB, 10); // 关蜂鸣器 } { /* 配置TMR0 */ NVIC_DisableIRQ(TMR0_IRQn); // 第一步 使能和选择定时器时钟源及使能定时器模块 SYSCLK->CLKSEL1.TMR0_S = 0; // 选择12Mhz作为定时器时钟源 SYSCLK->APBCLK.TMR0_EN =1; // 使能定时器0 TIMER0->TCSR.CEN = 1; // 使能定时器模块 // 第二步 选择操作模式 TIMER0->TCSR.MODE = 1; // 选择周期模式 TIMER0->TCSR.CRST = 1; // 清加1计数器 // 第三步 输出时钟周期 = 定时器时钟源周期*(8位预分频因子 + 1) * (24位比较因子TCMP) TIMER0->TCSR.PRESCALE = 11; // 12分频 TIMER0->TCMPR = 5000; // 12M/12/5000=200Hz, 5ms // 第四步 使能中断 TIMER0->TISR.TIF = 1; // 清中断 TIMER0->TCSR.IE = 1; // 使能中断 NVIC_EnableIRQ(TMR0_IRQn); // 使能TMR0中断 // 第五步 使能定时器模块 TIMER0->TCSR.CRST = 1; // 复位向上计数器 TIMER0->TCSR.CEN = 1; // 使能TMR0 //TIMER0->TCSR.TDR_EN=1; // 无需读取加1计数器值 } { /* 配置PWM0 */ NVIC_DisableIRQ(PWMA_IRQn); // 第一步,GPIO初始化 SYS->GPAMFP.PWM0_AD13=1; // 第二步,使能和选择PWM时钟源 SYSCLK->CLKSEL1.PWM01_S = 0; // 选择12MHz作为PWM时钟源 SYSCLK->APBCLK.PWM01_EN = 1; // 使能PWM时钟 // PWM clock = clock source/(Prescale + 1)*divider(分数形式) PWMA->PPR.CP01=119; // [0, 255] 预分频Prescale, 置零则停止输出时钟 // 120分频 PWMA->CSR.CSR0=4; // 时钟次分频clock divider->0:/2, 1:/4, 2:/8, 3:/16, 4:/1 // 所得时钟频率为12M/120/1=100kHz, 10us // 此处频率与CNR每一等份时间相对应 // 第三步,选择PWM操作模式 PWMA->PCR.CH0MOD=1; // 0: 单次触发模式, 1: 自动重载模式 // 设置CH1MOD为从0到1后,CNR和CMR会被自动清零 PWMA->PCR.CH0INV=0; // 反向使能->0:失能, 1:使能 PWMA->PCR.DZEN01=0; // 死区发生器使能->0:失能, 1:使能 /* 第四步,选择PWM频率计占空比 */ PWMA->CNR0=1; // 定时器载入值 [0,65535] // PWM周期=PWM clock/(CNR+1) // =1*10us PWMA->CMR0=PWMA->CNR0>>1; // PWM比较值,[0,65535] // PWM占空比 = (CMR+1)/(CNR+1) // CMR >= CNR 输出高电平 // 第五步,使能PWM输出 PWMA->PCR.CH0EN=0; // 使能PWM核:0,失能;1使能;此处先失能,留作以后效果比对 PWMA->POE.PWM0=1; // 输出到引脚:0,失能;1,使能 // 第六步 中断设置 PWMA->PIER.PWMIE0=0; // 使能中断:0,失能;1,使能 // NVIC_EnableIRQ(PWMA_IRQn); } LOCKREG(); } /********************************************************** * TMR0 ISR **********************************************************/ void TMR0_IRQHandler(void) __irq { // 注意:ISR内必须清中断 TIMER0->TISR.TIF = 1; // 清中断 g_tmr0_5ms = YES; } /********************************************************** * UART0 回调函数 **********************************************************/ void PFN_UART_CALLBACK(void) { // 注意:回调函数内无须清中断 switch(GetChar()) { case '1': g_breathing_led = NO; // 失能呼吸灯 PWMA->POE.PWM0=0; // 输出到引脚:0,失能;1,使能 PWMA->PPR.CP01=40; // 40分频 PWMA->CSR.CSR0=3; // 12M/40/16 = 10kHz, 100us PWMA->CNR0=9999; // PWM周期=10000*100us=1s PWMA->CMR0=PWMA->CNR0>>1; // PWM占空比= (CMR+1)/(CNR+1)=50% PWMA->PCR.CH0EN=1; // 使能PWM核:0,失能;1使能 PWMA->POE.PWM0=1; // 输出到引脚:0,失能;1,使能 PRINT("亮灭灯:频率1Hz,占空比50%\r"); break; case '2': g_breathing_led = YES; // 使能呼吸灯 PWMA->POE.PWM0=0; // 输出到引脚:0,失能;1,使能 PWMA->PPR.CP01=119; // 分频 PWMA->CSR.CSR0=1; // 12M/120/1 = 100kHz, 10us PWMA->PCR.CH0EN=1; // 使能PWM核:0,失能;1使能 PWMA->POE.PWM0=1; // 输出到引脚:0,失能;1,使能 g_duty_cnt = 0; PRINT("呼吸灯:频率1.25Hz,占空比自己算\r"); break; case 'q': g_breathing_led = NO; // 失能呼吸灯 PWMA->PCR.CH0EN=0; // 使能PWM核:0,失能;1使能 PWMA->POE.PWM0=0; // 输出到引脚:0,失能;1,使能 PRINT("关灯\r"); break; default: break; } } /********************************************************** * 主函数 **********************************************************/ int main(void) { MAIN_INIT(); // 上电初始化系统 while(1) { if(g_tmr0_5ms != NO) { g_tmr0_5ms = NO; if(g_breathing_led != NO) { if(g_duty_acc_dec != NO){ g_duty_cnt ++; if(g_duty_cnt == 199) g_duty_acc_dec = NO; } else { g_duty_cnt --; if(g_duty_cnt == 0) g_duty_acc_dec = YES; } PWMA->CNR0=199; // PWM周期=200*10us=2ms // 呼吸灯周期=2ms*200=0.4s,1.25Hz PWMA->CMR0=g_duty_cnt; // PWM占空比= (CMR+1)/(CNR+1)=? } } if(0) break; // 跳出大循环 } DrvUART_Close(UART_PORT0); return 0; }