单片机
返回首页

ARM 学习笔记七 (PWM试验)

2016-05-04 来源:eefocus

PWM-脉宽调制。我的理解是通过控制一个计数器来实现不同脉宽的输出,从而驱动蜂鸣器发出不同的声音。

本实验用到的寄存器如下:

GPBCON -- I/O端口配置寄存器 B
           00=Input 01=Output 10=PWM 11=Reserved

GPBDAT -- I/O端口数据寄存器 B

GPFUP --I/O端口上拉电阻使能寄存器 B
          0=Enable 1=Disabled
          
TCFG0 -- 时钟配置寄存器0 (主要是设置预分频)
定时器输入时钟频率=PCLK/{预分频}/{分割值}
{预分频}=1~255
{分割值}=2,4,8,16,32
Address: 0x51000000
Reset value:0x00000000
[7:0]:These 8 bits determine prescaler value for Timer0 and 1

TCFG1 --时钟配置寄存器1(主要是设置分割值)
Address: 0x51000004
Reset value:0x00000000
[3:0]:Select MUX input for PWM Timer0
      0000=1/2  0001=1/4 0010=1/8
      0011=1/16 01XX=External TCLK0

TCON -- 时钟控制寄存器
Address:0x51000008
Reset value:0x00000000
[0]: Determine start/stop for Timer0 (设置启动/停止)
     0=Stop 1=Start for Timer0
[1]: Determine the manual update for Timer0 (设置允许手动修改计数)
     0=No operation 1=Update TCNTB0 & TCMPB0
[2]: Determine the output inverter on/off for Timer0 (设置中断)
     0=Inverter off 1=Inverter on for TOUT0
[3]: Determine auto reload on/off for timer0
     0=One-shot 1=Interval mode(auto reload)
[4]: Determine the dead zone operation
     0=Disable 1=Enable


TCNTB0 -- timer0计数缓存寄存器
Address:0x5100000c
Reset value:0x00000000
[15:0]:Set count buffer value for Timer0

TCMPB0 -- timer0比较缓存寄存器
Address:0x51000010
Reset value:0x00000000
[15:0]:Set compare buffer value for Timer0

实验方法:
1.按下k1键,使TOUT0递增占空比

2.按下k2键,使TOUT0递减占空比

3.按下k3键,停止输出

//代码1---参考阿南的例子,这个简单

#include '2410addr.h'


#define KEY1   (1<<1)  // rGPF[1] =1 ;
#define KEY2   (1<<4)  // rGPF[4] =1 ;
#define KEY3   (1<<2)  // rGPF[2] =1 ;

unsigned short freq =0;

//===========================[ SYSTEM ]===================================================
//static int delayLoopCount = 400;
static int delayLoopCount = FCLK/10000/10;

void Delay(int time)
{
      // time=0: adjust the Delay function by WatchDog timer.
      // time>0: the number of loop time
      // resolution of time is 100us.
    int i,adjust=0;
    if(time==0)
    {
        time   = 200;
        adjust = 1;
        delayLoopCount = 400;
            //PCLK/1M,Watch-dog disable,1/64,interrupt disable,reset disable
        rWTCON = ((PCLK/1000000-1)<<8)|(2<<3);
        rWTDAT = 0xffff;                              //for first update
        rWTCNT = 0xffff;                              //resolution=64us @any PCLK
        rWTCON = ((PCLK/1000000-1)<<8)|(2<<3)|(1<<5); //Watch-dog timer start
    }
    for(;time>0;time--)
        for(i=0;i     if(adjust==1)
    {
        rWTCON = ((PCLK/1000000-1)<<8)|(2<<3);   //Watch-dog timer stop
        i = 0xffff - rWTCNT;                     //1count->64us, 200*400 cycle runtime = 64*i us
        delayLoopCount = 8000000/(i*64);         //200*400:64*i=1*x:100 -> x=80000*100/(64*i)  
    }
}

void PWM_Ini(unsigned short cycle,unsigned short duty){

   if(duty > cycle) duty = cycle;
  
   //定时器输入时钟频率=PCLK/{预分频}/{分割值}   
   rTCFG0 =0x64; //设置预分频为100
   rTCFG1 =0x0;//设置分割值为1/2
  
   rTCNTB0 =cycle; 
   rTCMPB0 =duty;
  
 
   rTCON = 0x0A;//disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, stop timer 0
    //000 01010
   rTCON = 0x09;//disable deadzone, auto-reload, inv-off, no operation, start timer 0
   //1001    
}

void PWM_Stop(){
    freq = 2000;
    rGPBCON = 0x0;
    rGPBDAT = 0x0;   
}

void PWM_Output(int number){
  
   if ( number > 0 ){
      if ( freq < 255 )
         freq += 16;
      else
         freq = 0;
   }
     
   if ( number < 0 ){
      if ( freq >= 16 )
         freq -= 16;
      else
         freq = 0;
   }
  
   rTCMPB0=freq; 
}
   
void Main(void){    

    rGPFUP = 0xf800;//  KEY [7:0] => PU En
    rGPFCON=0xfcc0; //KEY[7:0] => INPUT;
   
    rGPBCON = 0x0;
    rGPBCON = 0x02;//TOUT0为输出端口
   
    PWM_Ini(255,freq);
  
   Delay(0);
  
    while (1){
   
    if ( !(rGPFDAT & KEY1) ){
       Delay(1000);
       //if (rGPGDAT & KEY1) continue;
       PWM_Output(1);
    }
      
    if ( !(rGPFDAT & KEY2) ){
       Delay(1000);
       //if (rGPGDAT & KEY2) continue;
       PWM_Output(-1);
    }
   
    if ( !(rGPFDAT & KEY3) ){
       Delay(1000);
       //if (rGPGDAT & KEY3) continue;
       PWM_Stop();      
    }
      
   }
 }

上面的例子就是按键去抖功能没能调试出来。

//代码2---参考开发板带的例子,这个不好理解

#define GLOBAL_CLK  1

#include 'def.h'
#include '2440addr.h'
#include 'option.h'


#define KEY1  (1<<1) // rGPF[1] =1 ;
#define KEY2  (1<<4) // rGPF[4] =1 ;
#define KEY3  (1<<2) // rGPF[2] =1 ;


static unsigned int freq;

void PWM_Ini(){

    ChangeClockDivider(3,1); //获得PCLK=67.5MHz
    ChangeMPllValue(127,2,1);
   
    rGPFUP = 0xf800;//  KEY [7:0] => PU En
    rGPFCON=0xfcc0;  //KEY[7:0] => INPUT;
}

void PWM_Stop(){
    freq = 2000;
    rGPBCON = 0x0;
    rGPBDAT = 0x0;   
}

void PWM_Output(int number){

    rGPBCON = 0x0;
    rGPBCON = 0x02;//TOUT0为输出端口
    //rGPBUP  = 0x01;//禁止上拉电阻
   
    //定时器输入时钟频率=PCLK/{预分频}/{分割值}   
    rTCFG0 =0x64; //设置预分频为100
    rTCFG1 =0x0;//设置分割值为1/2 
    
  
   if ( number > 0 ){
      if ( freq < 20000 )
         freq += number;
      else
         freq = 2000;
   }
     
   if ( number < 0 ){
      if ( freq > 300 )
         freq -= number;
      else
         freq = 2000;
   }
     
  
   rTCNTB0 =freq; 
   rTCMPB0 =rTCNTB0>>1;
  
 
   rTCON = 0x0B;//disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
    //000 01011
   rTCON &= ~2;//clear manual update bit
}
   
void Main(void){

    freq = 2000;
   
    PWM_Ini();
  
    while (1){
   
    if ( !(rGPFDAT & KEY1) )
       PWM_Output(100);
      
    if ( !(rGPFDAT & KEY2) )
       PWM_Output(-100);
   
    if ( !(rGPFDAT & KEY3) )
       PWM_Stop();      
      
   }
 }

这个例子每次都要重新设置PGB端口。

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 家用电源无载自动断电装置的设计与制作

  • 用数字电路CD4069制作的万能遥控轻触开关

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 开关电源的基本组成及工作原理

  • 用NE555制作定时器

  • 带有短路保护系统的5V直流稳压电源电路图

    相关电子头条文章