矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
楼主: wey05

帮助新手学习单片机数码管电子钟:原理,c程序详解

  [复制链接]
     
发表于 2011-4-23 22:29:12 | 显示全部楼层

回复 34# millwood 的帖子

多谢millwood 兄和Paktu兄的回复,小弟这里表示下感谢啦,
呵呵 小弟还不会avr,有待以后探索啦 :),加油!
回复 支持 反对

使用道具 举报

发表于 2011-4-24 00:19:19 | 显示全部楼层
2.设置定时器1为PWM方式:
TCCR1A=0xA2;


a big part of writing robust and efficient code is to make it so that it is bug-free. that means that you should write code so that it can be easily understood and reused once it is debugged. the above code for example is tough to understand and varies from device to device - you want to minimize that as much as you can.

setting up a pwm is nothing but determining a few simple elements: the period of the pwm, its duty cycle, its output pins, and how it should behave once the duty cycle is reached.

here is what I do to set up a pwm:

        //manually set up the pwm
        TMR1_PS(TMR_PS_OFF);                                                //turn off tmr1

        //set the mode for the timer
        TMR1_MODE(TMR_MODE_FASTPWM_ICR);                        //set the pwm mode, top in icr

        //set up ch a for pwm period
        TMR1_ICR_TOP(10);                                                        //set the period
        //TMR1_OCA_ACT(OC_CLR);                                                //clear oca pin on output match
        //TMR1_OCA_PIN(ENABLE);                                                //enable oca pin output

        //set ch b
        TMR1_OCB_TOP(10-1);                                                        //set the duty cycle
        TMR1_OCB_ACT(OC_CLR);                                                //clear oca pin on output match
        TMR1_OCB_PIN(ENABLE);                                                //enable oca pin output

        //reset the timer/counter
        //TMR1_CNT_SET(0);                                                        //reset the timer / counter

        //set the prescaler and turn on the timer
        TMR1_PS(TMR_PS_1x & TMR_PS_MASK);                        //set up the prescaler
        //ei();                                                                                //enable interrupt


it relies on a set of macros that are device dependent and easy to read. so when I port the code to a different chip, all I need to do is to rewrite the macros for that chip. Over time, this approach allows me to accumulate a large collection of code pieces that I can reuse in other projects.
回复 支持 反对

使用道具 举报

发表于 2011-4-24 00:48:10 | 显示全部楼层
here is an advantage to hardware pwm: the above code has a pwm period of 10 ticks, and a duty cycle of 90% (9 ticks / 10 ticks). on a avr running at 1Mhz cpu, each tick is 1 ticks,  yield a pwm period of 10us -> 100khz.

obviously, you lose resolution in this case.
回复 支持 反对

使用道具 举报

     
发表于 2011-4-24 09:42:58 | 显示全部楼层
To millwood@F38:

Of course,it is depend on your compiler. I using Code Vision AVR C's CodeWizardAVR to genetate the codes:
The STM32 FirmWare have this style.

[ 本帖最后由 Paktu 于 2011-4-24 09:44 编辑 ]
CODE.PNG
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-4-24 21:18:54 | 显示全部楼层

关于定时器T2简介

好几位先生就T2定时器的应用讨论了一会,其实要注意,定时器T2只是存在于52单片机中,也就是52单片机有3个定时计数器T0 T1 T2.。51机只有T0 T1。
T2有什么效益呢,除了提供串口通信的波特率发生器,以及可以输出定时脉冲外,它最大的优点就是可以实现16位预置值的自动装载。我们知道,T0 T1在16位定时计数时,计数满了溢出,计数器变成0,要想继续工作得靠程序重新给计数器输入预置值,从发生中断、输入预置值、再次启动都要耗费时间,额外时间的累积最终是使得时钟减慢。
现在如果用52机的T2定时器,如果把它设置在16位自动重置方式的话,那么一旦计数满溢出它就自动由硬件重新加入预置值,而不需要耗费额外时间,因此计时的准确性大大提高了。不过也只是52能享用这个特性了。T2定时器在自动装载方式如以下简图所示:
timer2.JPG

编程时,要计算好定时所需的预置值,高8位存入TH2和RCAP2H,,低8位存入TL2和RCAP2L。控制寄存器T2MOD和T0 T1的TMOD不同,仅仅只有最低两位而且还是控制外部计数的,定时时它设为0,控制寄存器T2CON则作用较大,CT2为0时,内部脉冲计时,TR2置1就启动定时,置0就停止定时,启动后,从预置值往上(或往下)计数,到达溢出时,中断标志TF2置位同时自动把RCAP2H和RCAP2L保存的预置值存入TH2和TL2,再次开始定时。
预置值的计算,例如采用11059200晶振,则
TH2=(65536-t*11059200/12000000)/256;
TL2=(65536-t*11059200/12000000)%256
具体数值由编译器计算。使用T2以后,电子钟的变成会更加简单。

[ 本帖最后由 wey05 于 2011-4-24 21:23 编辑 ]
回复 支持 反对

使用道具 举报

     
发表于 2011-4-24 21:38:27 | 显示全部楼层

回复 41# wey05 的帖子

“到达溢出时,红缎标志TF2置位”,在中断函数里 必须软件清零TF2,
另外根据34#的讲述,在内部定时器的中断里依靠
拉高拉低IO管脚电平来输出方波的方式, 所能达到的最高频率在50Khz左右(12Mhz晶阵情况下),millwood兄是这样吧?

赞下wey05兄的好帖:)
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-4-24 21:52:59 | 显示全部楼层
原帖由 xiaomu 于 2011-4-24 21:38 发表
“到达溢出时,红缎标志TF2置位”,在中断函数里 必须软件清零TF2,
另外根据34#的讲述,在内部定时器的中断里依靠
拉高拉低IO管脚电平来输出方波的方式, 所能达到的最高频率在50Khz左右(12Mhz晶阵情况下),mil ...

TF2必须由中断响应程序清除,这个非常非常重要,如果你不清除,那么它继续计数,预置值已经不起作用。你不管设置什么预置值,他都以一个没有明显变化的定时工作。我做过这个试验。输出方波最大50千赫?没有试过
回复 支持 反对

使用道具 举报

发表于 2011-4-25 00:45:21 | 显示全部楼层
"在中断函数里 必须软件清零TF2,"

you will have to read the datasheet about that. for regular 8051, the flags are automatically cleared when the execution jumps to the isr.

"所能达到的最高频率在50Khz左右(12Mhz晶阵情况下),millwood兄是这样吧?"

that's assuming that you don't use hardware pwm. using hardware pwm can in theory reach higher frequencies.
回复 支持 反对

使用道具 举报

发表于 2011-4-25 00:58:07 | 显示全部楼层
你不管设置什么预置值,他都以一个没有明显变化的定时工作。


that's because it loops in the isr, if you don't clear the flag. TF2 in a 8052 has to be cleared by software, unlike TF0/TF1.
回复 支持 反对

使用道具 举报

     
发表于 2011-4-25 22:18:52 | 显示全部楼层

回复 14# 的帖子

millwood兄
TH0 = MSB(-period);  //load the msb for a count-up timer
TL0 = LSB(-period);  //load the lsb for a count-up timer
这里的负号是不是 让period计算时取补码,
这样相当于65536+(-tx*f/1200000)
回复 支持 反对

使用道具 举报

     
发表于 2011-4-25 23:16:27 | 显示全部楼层

回复 14# millwood 的帖子

用KEIL
仿真了下,原来编译器也挺聪明的,编译时候就已经分析到结果了

------------------------------------------
我写的代码
#include "reg52.h"
#define F_XTAL 12000000ul //xtal for a 12-state 8051
#define MSB(word_t) ((word_t) >> 8) //msb of a word
#define LSB(word_t) ((word_t) & 0x00ff) //lsb of  a word
void main(void)
{

         unsigned char tx=50,a ,b;
        unsigned char  period=(F_XTAL / 12000000) * tx; //period for the timer
        TH0 = MSB(-period);  //load the msb for a count-up timer
        TL0 = LSB(-period);  //load the lsb for a count-up timer
}
----------------------------------------------
翻译出的汇编代码:
C:0x0003    7F32     MOV      R7,#0x32
    11:         unsigned char   period=(F_XTAL / 12000000) * tx; //period for the timer
    12:         TH0 = MSB(-period);  //load the msb for a count-up timer
C:0x0005    EF       MOV      A,R7
C:0x0006    F4       CPL      A
C:0x0007    04       INC      A
C:0x0008    758C00   MOV      TH0(0x8C),#0x00
    13:         TL0 = LSB(-period);  //load the lsb for a count-up timer
C:0x000B    F58A     MOV      TL0(0x8A),A
答案是TH0=0,TL0=0xCE(16进制数),完全正确。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-4-26 00:27:11 | 显示全部楼层
直接就
TH2=(65536-T*F/12000000)/256; //T:定时,微秒,F,晶振,赫兹,12分频
TL2=(65536-T*F/12000000)%256
即可

[ 本帖最后由 wey05 于 2011-4-26 00:28 编辑 ]
回复 支持 反对

使用道具 举报

发表于 2011-4-26 00:41:05 | 显示全部楼层
这里的负号是不是 让period计算时取补码,


that's correct.

it assumes that the compiler follows 2's complements - pretty much all of them do that.

and we want to use the complements because of the count-up nature of the timer/counter and the interrupt happens when the counter overflows.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-4-26 00:44:25 | 显示全部楼层

还是接着讲C语言入门吧

一个程序语言都是有一些基本要素构成,如同造房子,也就是由砖瓦灰砂石根据要求反复搭配而成
C语言最基本的东西,就是变量,还有少数常量,然后就是运算符,表达式,加上变量构成语句。
语句再和变量等等结合起来,构成功能块:函数。最后用多个函数形成总的功能:程序。
C语言的格式有规定的,如同写书信,有抬头,内容,落款,不可任意。
一个完整的C程序是怎样的呢?大体来说,它分为几个区块或段落,我们先有个总体认识再来研究细节。这个和教材的讲法有点不同,看官一时不明白也不要紧,咱们如同学游泳,慢慢来。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-4-27 16:22:39 | 显示全部楼层

C语言的变量,运算符,表达式语句

上次已经说了C中的变量。变量到底是什么东西?实际上它就是一个内存地址,假设我们用符号i表示一个变量,那也就是给它定下一个地址,那么这个变量是多少,随我们往这个地址里面加入的数据而定。特别要注意的是程序中,变量必须先声明,后使用。如同房客去住旅馆,得得先找大堂,要房间,然后方可入住。要不然里面已经住人,岂能容你强行闯入?定义的方法就是如下这样:(双斜杠右边都是注释文字仅仅其说明作用,不影响程序)
unsigned char i;   //无符号字符型变量i:它只能是数,最小是0,最大是255
int k;                       //有符号整型变量k;
char ABC;             //字符变量ABC,他可以是有符号数,也可以是单个字符
注意C的大小写不一样的,和basic不同。
有了变量,再加上运算符,意义就深入一步了:
i = 20;   // 把20赋予变量i,这里=不是等于号,而是赋值号,这就是个使用最多的运算符。最后一定加上英语的分号;汉语的分号;可不行!规矩就是规矩。这么简单的一个式子,加上英语分号就是一个赋值语句了我再完整写一下:
unsigned char i;  //先声明变量
i=20;                      //再给变量赋值,已经是一个表达式语句了
或者合二为一:
unsigned char i=20; //一边申明一边就赋值了,这也叫赋初值。
绝不可以还没声明呢就来赋值
m=20;  //m是什么?没有声明,那谈何赋值呢
赋值运算符是用得最多的,给变量赋值以后还可以多次赋值。
还有算术运算符:+  -  *  /  %   (相除以后取余数)
自增自减运算符 ++   --     是把变量加1或减1,例如unsigned char  i=20; i++;  那么i就是21l了。
关系运算符:>  >=  <  <=  ==  != //==才是等于 !=是不等于,他们用来比较两个变量或算式。
逻辑运算符:||    &&    !   //逻辑  或  与  非
位运算符: ~   <<    >>   &    |   ^  //位取反, 左移   右移  位与  位或 位异或
这个用得较多,举几个例子,当然都是对变量操作
unsih\gned char  a=0XFE; //也就是2进数  0b 1111 1110
a=~a;    //a位取反后再赋予a;各位0变1,1变0,相当于a变成0b 0000 0001.然后左移1位
a=a<<1; //最高位0移出去,最低位移进来1个0,a变成 0b  0000 0010,再左移2位
a=a<<2;  //a变成    0b 0000 1000  注意移出去的就没有了,移进来的总是0然后位与0XFC
a=a&0XFC;//0XFC就是2进 0b 1111 1100; a原已是0b 0000 1000 结果a为  0b 0000 1000
a=a|0XC9; //就是0b 0000 1000和0b 1100 1001位或,结果a成了0b 1100 1001
a=a^0X78;//就是0b 1100 1001 和0b 0111 1000位异或,结果a变成0b 1011 0001
记住位反:1变0,0变1.。位与:11得1,10得0,00得0。位或11得1,10得1,00得0.。位异或:11得0,10得1,00得0.。
位运算使用较多,例如在8位数码管的依次显示,实际就是个移位操作例如用P2口控制
P2=0X01 ;// 最右端:秒个位亮
P2=0X02;//秒十位亮,个位灭
P2=0X04,;//分个位亮,秒十位灭,如果要点8个管,就要写8个语句,太累了!
也可以这样
unsigned char a=0x01;//
P2=a;                              //P2点亮秒个位
a=a<<1;
P2=a;                             //P2点亮秒十位,以后我们会通过循环操作就不要写8个句子了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2025-4-27 19:15

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表