这次51的代码本来是自己写的,不过实在是遇到了点悲剧,时间没算好,还是看这个教程代码,很整洁的
这次用到了定时器,对于现在的52系列有3个定时器,并且Timer0,1,2.并且2有特殊的功能(尤其串口。以后讨论)
其中前面用到了很多定时器有关的设置方面的东西。只要有51的书都会涉及的。
首先说一下我很想放图片进来,但是没有找到很好的方法。我会尝试找一下找到了,我就放上来
这次时间间隔比上次长很多,因为430的Timer学的时间长了点。。。并且现在还需要不断复习和反复看要不马上就不会用了(还有英语考试)
好了,先做笔记吧
#include
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=16;
}
for(i=0;i
{
dispbuf=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
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
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
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
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
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
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<
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=0x00;
}
for(i=0;i
{
dispbuf=num;
}
}
}
完事了,调试更改过的代码就是这个。要好好努力啊。嗯嗯。下一次,争取做一下LCD的实现(本质就是这个),然后串口吧