3)51/MSP430数码管的拉幕式效果

来源:互联网 发布:卖家中心登陆淘宝网 编辑:程序博客网 时间:2024/04/30 14:52

这次51的代码本来是自己写的,不过实在是遇到了点悲剧,时间没算好,还是看这个教程代码,很整洁的

这次用到了定时器,对于现在的52系列有3个定时器,并且Timer0,1,2.并且2有特殊的功能(尤其串口。以后讨论)

其中前面用到了很多定时器有关的设置方面的东西。只要有51的书都会涉及的。

首先说一下我很想放图片进来,但是没有找到很好的方法。我会尝试找一下找到了,我就放上来

这次时间间隔比上次长很多,因为430的Timer学的时间长了点。。。并且现在还需要不断复习和反复看要不马上就不会用了(还有英语考试)

好了,先做笔记吧

#include <AT89X51.H>
unsigned char code dispcode[]={0x3f,0x06,0x5b,0x4f,
                               0x66,0x6d,0x7d,0x07,
                               0x7f,0x6f,0x77,0x7c,
                               0x39,0x5e,0x79,0x71,0x00};
unsigned char dispbitcode[]={0xf8,0xf9,0xfa,0xfb,
                             0xfc,0xfd,0xfe,0xff};   //0~7发送到P1.0~1.2  
unsigned char dispbuf[8]={16,16,16,16,16,16,16,16};
unsigned char dispbitcnt;
unsigned int t4mscnt;       //每个数码管显示的时间大约4ms    
unsigned char tmscnt;     //每隔1ms,就刷新一个数码管  
unsigned char u;
unsigned char i;
 
void main(void)
{
  TMOD=0x02;  //模式2,就是自动装填的模式,关于模式很多参考书都有
  TH0=0x06;   // 保存初值
  TL0=0x06;
  TR0=1;  //启动定时器0
  ET0=1;  //使能Timer 0
  EA=1;  //开启中断
  while(1);
}
 
void t0(void) interrupt 1 using 0
{
  tmscnt++;
  if(tmscnt==4)  //计算下,大概时间就是稍大于4*250us
    {
      tmscnt=0;
P1=0xff;      //关闭所有显示
      P0=dispcode[dispbuf[dispbitcnt]];
      P1=dispbitcode[dispbitcnt];
      dispbitcnt++;
      if(dispbitcnt==8)
        {
          dispbitcnt=0;
        }
    }
 t4mscnt++;
  if(t4mscnt==1600) //每个显示时间就是大概1600*250us。不过显然不准,因为好多指令需要消耗时间
    {
    t4mscnt=0;
      u++;
      if(u==9)
        {
          u=0;
        }
      for(i=0;i<8;i++)
        {
          dispbuf[i]=16;   
    }
      for(i=0;i<u;i++)  //很关键,显示几个数字的功能
        {
          dispbuf[i]=i+1;      
        }
    }
}

##########################################################################

汇编贴上来供大家欣赏,读一读不是特别的费力,后面的流程写的非常清晰,供大家了解一下。

DISPBUF         EQU 30H

DISPCNT         EQU 38H

DISPBIT         EQU 39H

T1CNTA          EQU 3AH

T1CNTB          EQU 3BH

CNT             EQU 3CH

                ORG 00H

                   LJMP START

                ORG 0BH

                LJMP INT_T0

START:         MOV DISPCNT,#8

                MOV R1,#DISPBUF          ;直接寻址单元送寄存器

LP:             MOV A,#10                 ;改:从在MOV R1,#DISPBUF上面调到其下面

                MOV @R1,A                ;@R1为内部RAM单元

                INC R1 ; +1

                DJNZ DISPCNT,LP           ;DISPCNT减1不为零转移

DISPBUF  10    10   10    10   10   10    10    10

30H    30H  31H  32H  33H  34H  35H   36H  37H

DISPCNT  8     7    6     5     4    3    2     1

                MOV DISPBIT,#00H

                MOV T1CNTA,#00H

                MOV T1CNTB,#00H

                MOV CNT,#00H

                MOV TMOD,#01H

                MOV TH0,#(65536-1000) / 256

                MOV TL0,#(65536-1000) MOD 256 ;给定时器初值

                SETB TR0

                SETB ET0

                SETB EA

                SJMP $

INT_T0:

                MOV TH0,#(65536-1000) / 256

                MOV TL0,#(65536-1000) MOD 256

MOV A, #0FFH      ;关闭所有显示

                MOV P1,A

                MOV A,DISPBIT

                ADD A,#DISPBUF

                MOV R0,A

                MOV A,@R0

                MOV DPTR,#TABLE

                MOVC A,@A+DPTR

                MOV P0,A

                MOV A,P1

                ANL A,#0F8H

                ADD A,DISPBIT

                MOV P1,A               

                INC DISPBIT

                MOV A,DISPBIT

                CJNE A,#08H,NEXT

                MOV DISPBIT,#00H

NEXT:           INC T1CNTA

                MOV A,T1CNTA

                CJNE A,#50,LL1

                MOV T1CNTA,#00H

                INC T1CNTB

                MOV A,T1CNTB

                CJNE A,#8,LL1

                MOV T1CNTB,#00H

                INC CNT

                MOV A,CNT

                CJNE A,#9,LLX

                MOV CNT,#00H

                MOV A,CNT

LLX:            CJNE A,#01H,NEX1

                 MOV 30H,#1

LL1:            LJMP DONE

NEX1:           CJNE A,#02H,NEX2

                MOV 31H,#2

                MOV 30H,#1

                LJMP DONE

NEX2:           CJNE A,#03H,NEX3

                MOV 32H,#3

                MOV 31H,#2

                MOV 30H,#1

                LJMP DONE

NEX3:           CJNE A,#04H,NEX4

                MOV 33H,#4

                MOV 32H,#3

                MOV 31H,#2

                MOV 30H,#1

                LJMP DONE

NEX4:           CJNE A,#05H,NEX5

                MOV 34H,#5

                MOV 33H,#4

                MOV 32H,#3

                MOV 31H,#2

                MOV 30H,#1

                LJMP DONE

NEX5:           CJNE A,#06H,NEX6

                MOV 35H,#6

                MOV 34H,#5

                MOV 33H,#4

                MOV 32H,#3

                MOV 31H,#2

                MOV 30H,#1

                LJMP DONE

NEX6:           CJNE A,#07H,NEX7

                MOV 36H,#7

                MOV 35H,#6

                MOV 34H,#5

                MOV 33H,#4

                MOV 32H,#3

                MOV 31H,#2

                MOV 30H,#1

                LJMP DONE

NEX7:           CJNE A,#08H,NEX8

                MOV 37H,#8

                MOV 36H,#7

                MOV 35H,#6

                MOV 34H,#5

                MOV 33H,#4

                MOV 32H,#3

                MOV 31H,#2

                MOV 30H,#1

                LJMP DONE

NEX8:           CJNE A,#00H,DONE

                MOV 37H,#10

                MOV 36H,#10

                MOV 35H,#10

                MOV 34H,#10

                MOV 33H,#10

                MOV 32H,#10

                MOV 31H,#10

                MOV 30H,#10

LL:             LJMP DONE

DONE:           RETI

TABLE:          DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH,00H

                END

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

接下来写MSP430的代码啦~首先先简单看看430的TIMER们~

由于430是学习的主要,所以定时器就多了点,首先是看门狗定时器
看门狗定时器WDT
看门狗复位模式
程序功能:利用WDT看门狗实现LED的闪烁,闪烁时间大
约是60000/1M=60ms
#include <msp430x44x.h>
void main (void )
{
unsigned i;
WDTCTL=WDTPW+WDTHOLD; //停止看门狗
P5DIR |= BIT1; //P5.1为输出
for (i=0;i<60000;i++) //延时大约60ms
P5OUT &=~BIT1; //P5.1为低,灯灭
for (i=0;i<60000;i++) //延时大约60ms
P5OUT |= BIT1; //P5.1为高,灯亮
WDTCTL=WDTPW; //启动看门狗
while (1);
}
WDTCTL高八位被用作口令;低八位才是对WDT操作的控制命令。在读WDTCTL时不需要口令数据低字节为WDTCTL的值
高字节为69H。写入时必须要有正确口令,5AH。错误复位。其中寄存器SSEL(2位)选择时钟源,0选择SMCLK,1选择ACLK
由ISO,IS1(0,1位)可确定定时时间(具体查表)
CNTCL(3) 清除WDTCNT(使用很经常)
TMSEL(4)工作模式选择 0看门狗 1定时
NMI(5) 0复位端 1边沿触发非屏蔽中断输入
NMIES(6) 若NMI是1则为  0上升沿触发  1下降沿触发
HOLD(7) 0激活 1时钟禁止输入

看门狗定时器模式
程序功能:WDT定时器模式,通过定时控制LED,使其LED
250mS闪烁
#include <msp430x44x.h>
void main(void)
{
WDTCTL = WDT_ADLY_250; // 设置看门狗定时时间为250ms
IE1 |= WDTIE; // WDT使能
P5DIR |= BIT1; //设置P5.1 为输出
_EINT(); //系统中断允许
for (;;)
{
_BIS_SR(LPM3_bits); // 进入LPM3模式,这个属于低功耗范畴中断响应结束休眠,430的低功耗跟版本也有关,需要查资料,3是最常用的
_NOP(); // 验证
}
}
#pragma vector=WDT_VECTOR //中断源声明,中断向量的表示方式
__interrupt void watchdog_timer(void) //中断服务程序
{
P5OUT ^= BIT1; // P5.1输出取反
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
基础定时器Basic Timer
寄存器为BTCTL
BTDIV:预分频选择,0没有,1为256分频
BTSSEL:时钟源选择 0=ACLK 1=SMCLK;当BTDIV=1时,该位无效。时钟为ACLK/256
        宏定义为BT_fCLK2_ACLK(MCLK,ACLK_DIV256)
BTHOLD: 开始停止用。0运行
BTIPx:中断频率选择 8位的共有8种选择;宏定义2种方式,一种为BT_fCLK2_DIVxx,另一种为BT_ADLY_xx(1为1/1024,1000为1s)
BTFREQx:为LCD提供刷新频率BT_fLCD_DIVxx或者BT_fLCD_xx,下一个要学习的内容就是LCD,所以先不细说
BTCNT1
BTCNT2:8位计数器,不过很少用
中断位于IE2
BTIE
BTIFG
下面有一个实例实现125ms定时来控制P5.1口连接的LED闪烁,
闪烁时间由定时时间确定。
#include <msp430x44x.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
IE2 |= BTIE; // Enable BT interrupt
BTCTL = BTDIV+BTIP1+BTIP0; // BT=Fclk2/16=8Hz, CLK2=ACLK/256 16分频,具体查表,不过推荐用宏定义//BT_fCLK2_DIV16
P5DIR |= 0x02; // Set P5.1 to output direction
_EINT(); // Enable interrupts
for (;;)
{
_BIS_SR(CPUOFF); // Enter LPM0.同样可以用LPMO;语句
_NOP();
}
}
#pragma vector=BASICTIMER_VECTOR //BT定时器中断
__interrupt void basic_timer (void)
{
P5OUT ^= 0x02;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16位计数器TIMER_A
两部分组成:主计数器和比较/捕获模块
多个可选的计数器时钟源;
两种工作模式:定时器模式,比较捕获模式;
定时器模式下:4种计数模式
比较捕获模式下:8种输出,能够在无需CPU干预下自动根据触发条件捕获定时器计数值,或自动产生各种输出波形(如PWM调制、单稳态脉冲等)
主计数器的计数值存放在TAR寄存器中。每个比较/捕获模块还有一个独立的寄存器TACCTLx,以及一个比较值捕获值寄存器TACCRx(012)。在一般的应用中,TACCRx用于设定周期与占空比;在捕获模式下TACCRx存放捕获结果。
主计数器控制寄存器TACTL控制位:
TASSELx:Timer_A 计数器时钟源选择,00外部引脚(TACLK),01=ACLK 10=SMCLK 11=^TACLK
宏定义为TASSEL_x
IDx: Timer_A 计数器预分频系数
00=NULL 01=2 10=4 11=8
宏定义为 ID_0 ID_1 ID_2 ID_3
TACLR: Timer_A 计数器清零控制位 0=不清(default) 1=清0
MCx: Timer_A 计数器的计数模式
00=停止 01=增计数 10=连续增计数 11=增-减计数
宏定义:MC_x
TAIFG: Timer_A 计数器溢出标志(中断标志)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TACCR0存放比较值/捕获值
四种计数模式:
停止
增计数
连续增计数
增-减计数
由下面的图我们可以看的很清楚,在增计数模式下,超过TACCR0则溢出,并且不需要初值装载问题,非常适合产生周期性定时中断,只要改变TACCR0的值即可随意调整定时周期。
在连续计数模式下,超过0xFFFF后溢出。需要重新初值,连续模式一般在捕获模式下使用,用来确定事件发生的准确时间或者准确的时间间隔。
增-减模式下。反复中由1-0的过程产生中断。多用于PWM发生器。能够产生带死区的驱动波形,可以直接驱动半桥电路。(具体解释以后可能会有,要不百度也知道呵呵)
在增计数模式或者增-减模式下,向TACCR0写入0可停止计数器。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
接下来看下Timer_A的捕获模式(具体功能参照手册)
捕获/比较模块TACCTLx 控制位:
CCISx: 捕获源选择
CMx: 捕获模式
SCS: 同步或异步捕获
CCI: 捕获模块的输入信号电平(异步)
SCCI: 捕获模块的输入信号电平(同步)
CAP: 模块工作模式选择
COV: 捕获溢出标志
OUTMODx: 比较模块的输出模式选择
CCIE: 中断允许
OUT: 比较模块的输出电平控制
CCIFG: 中断标志
在捕获模式下:
用某个指定管脚(TAx)的输入电平跳变触发捕获电
路。将次时刻主计数器的计数值自动保存到相应的捕
获值寄存器中,用来测频率、周期占空比等。
比较模式下:
模块不断将自身的比较值寄存器与主计数器的计数值
比较,一旦相等就自动改变指定引脚(TAx)的输出电
平(有8种模式)。可以用来输出PWM调制、可变单稳
态脉冲等波形
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Timer_A 定时应用
程序功能:
用F449的内部TIMER_A CCR0的比较模式功能,来控
制P5.1口驱动LEDxxms闪烁。
#include <msp430x44x.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
TACTL = TASSEL0 + TACLR; // ACLK, clear TAR
CCTL0 = CCIE; // CCR0 interrupt enabled
CCR0 =xxxx;//数值根据时钟频率决定
P5DIR |= 0x02; // Set P5.1 to output direction
TACTL |= MC_1; // Start Timer_a in upmode
_EINT(); // Enable interrupts
for (;;)
{
_BIS_SR(CPUOFF); // CPU off
_NOP();
}
}
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
P5OUT ^= 0x02;
}

#!¥#@¥#%¥……%&……¥%……¥¥
输出模式:OUTMOD_x
模式0:输出模式
模式1:置位模式
模式2:PWM置位/翻转模式
模式3:PWM置位/复位模式
模式4:翻转模式
模式5:复位模式
模式6:PWM翻转/复位模式
模式7:PWM复位/置位模式
其中2,3,6,7不舍和输出单元0
程序功能:
用F449的内部TIMER_A 的CCR0、CCR1和CCR2的比较模式
功能来实现周期是15.625mS,占空比分别为75%(P1.2)和
25%(p2.0)的两路PWM输出。
输出PWM调制波形,采用模式6(高电平PWM)或模式7(低电平
PWM),TACCRO 控制PWM方波周期,TACCRx 控制占空比。
#include <msp430x44x.h>
void main(void)
{
WDTCTL = WDTPW +WDTHOLD; // Stop WDT
FLL_CTL0 |= XCAP14PF; // Configure load caps
TACTL = TASSEL0 + TACLR; // ACLK, Clear Tar
CCR0 = 512-1; // PWM Period =512/32.768=15.625mS
CCTL1 = OUTMOD_7; // CCR1 reset/set
CCR1 = 384; // CCR1 PWM duty cycle
CCTL2 = OUTMOD_7; // CCR2 reset/set
CCR2 = 128; // CCR2 PWM duty cycle
P1DIR |= 0x04; // P1.2 output
P1SEL |= 0x04; // P1.2 TA1 out
P2DIR |= 0x01; // P2.0 output
P2SEL |= 0x01; // P2.0 TA2 out
TACTL |= MC_1; // Start Timer_A in up mode
for (;;)
{
_BIS_SR(LPM3_bits); // Enter LPM3
_NOP();
}
}
能实现双功能主要在于选择的输出模式,其中的等于TACCR0时置位保证周期的一致性,而等于TACCRx复位可以保证不同的PWM波。
Timer_A 中断
两个中断源:
TIMERA0_VECTOR 捕获通道0 具有最高的优先级
TIMERA1_VECTOR 捕获通道1,2,主计数器
还有跟Timer_A相似的Timer_B,后者增加了锁存器。具体参照User Guide的手册。
###################################################################################
连抄带学,马马虎虎了解430的时钟后我们来写同51功能的代码,依旧是C,在IAR中有反汇编的功能,因此可以使用这个功能参考汇编程序,精简指令的汇编是很复杂的。

#include <msp430x44x.h>
const unsigned char num[6]=
{0x14,0xcd,0x5d,0x1E,0x5b,0x00};
unsigned char led_buff[6];
unsigned char led_ctrl;
unsigned char dispbuf[6];
unsigned int t4mscnt;       //每个数码管显示的时间大约4ms     
unsigned char u;
unsigned char i;
unsigned char wait;
//void basic_timer();
void init(void){
  char tmp;
  P3DIR = 0xff; //设置P3输出
  P3OUT = 0x00; //设置初始值0
  P4DIR |= 0x03; //设置P4.0,P4.1输出
  P4OUT &= 0xfc;//设置初始值
  led_ctrl = 0;//用于控制哪个led显示
  for(tmp=0 ; tmp<6;tmp++)//初始化缓冲区
  {
  led_buff[tmp]=tmp;
  }
}
void led_display(){
  unsigned tmp ;
  tmp =0x01;
  P3OUT = dispbuf[led_buff[led_ctrl]];//设置显示值
  P4OUT |= 0x02;//打开数据锁存
  P4OUT &= 0XFD ;//关闭
  P3OUT = ~(tmp<<led_ctrl);//设定哪个led显示
  P4OUT |= 0X01;
  P4OUT &= 0xFE;
  led_ctrl = (led_ctrl + 1)%6;//设置下一个要显示的led 

}
void main(void)
{
  init();
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
IE2 |= BTIE; // Enable BT interrupt
BTCTL = BT_ADLY_1;
_EINT(); // Enable interrupts

for (;;)
{
_BIS_SR(CPUOFF); // Enter LPM0
_NOP();
}
}
#pragma vector=BASICTIMER_VECTOR //BT定时器中断
__interrupt void basic_timer (void)
{
led_display();
t4mscnt++;
if(t4mscnt==400)
{
    t4mscnt=0;
      u++;
      if(u==7)
        {
          u=0;
        }
      for(i=0;i<6;i++)
        {
          dispbuf[i]=0x00;   
    }
      for(i=0;i<u;i++)  //很关键,显示几个数字的功能
        {
          dispbuf[i]=num[i];      
        }
    }
}

完事了,调试更改过的代码就是这个。要好好努力啊。嗯嗯。下一次,争取做一下LCD的实现(本质就是这个),然后串口吧

最近如果有时间在用51尝试2262的软解码,希望能成功。

 

原创粉丝点击