xiaomu 发表于 2011-7-12 13:17:59

millwood 兄很热心 帮顶!!
而且你今天晚上提问,一觉想来 millwood 老师就把你的问题 回复了。

清风车影18 发表于 2011-7-12 17:04:46

Mr.millwood :
能否把自编的头文件内容公布一下让我等学习?
"gpio.h"
"rtc0.h"                                                               
"tmr1.h"                                                               
"_7seg.h"   
多谢

millwood 发表于 2011-7-12 21:38:29

you will need .c and .h files to make it work.

here is main.c//time keeping on 8051
//tmr0 runs as a 16-bit rtc (black roman approach)
//tmr1 controls the 7-segment display

#include <regx51.h>                                                                                        //we use keil c51
//#include <intrins.h>
#include "gpio.h"
#include "rtc0.h"                                                                                        //we use rtc0
#include "tmr1.h"                                                                                        //we use tmr1
#include "_7seg.h"                                                                                        //we use 7seg led display

//hardware configuration
//end hardware configuration

//global variables

void time2vRAM(void) {                                                                                //convert rtc0 to vram
        vRAM=_rtc0.hour/10;
        vRAM=_rtc0.hour%10;
        vRAM=0x10;                                                                                        //update hour display
        vRAM=_rtc0.min/10;
        vRAM=_rtc0.min%10;                                                                        //update minute display
        vRAM=(_rtc0.half_sec)?0x10:0x7f;                                                //???? - blinking the half second indicator
        vRAM=_rtc0.sec/10;
        vRAM=_rtc0.sec%10;//???????                                                 //update second display
}

void mcu_init(void) {
}

void main(void)      //???
{
        mcu_init();                                                                                                //reset the mcu
        _7seg_init();                                                                                        //reset the 7seg display
      
        //set up tmr0 to run as an rtc
        rtc0_init(RTC_500ms);                                                                        //reset tmr0 - period set by TMR0_PERIOD
        rtc0_act(time2vRAM);                                                                        //install new isr
      
        //set up tmr1 to run periodically to drive the display
        tmr1_init(1, TMR_1ms);                                                                         //reset tmr1 to trigger periodically
        tmr1_act(_7seg_display);                                                                //install custom isr
      
        ei();                                                                                                        //enable global interrupt

        while(1)
        {
                //delay();
        }
}

millwood 发表于 2011-7-12 21:40:09

here is the gpio.h (no gpio.c)//gpio header file for lpc21xx
#include <intrins.h>                                                        //we use _nop_()

#define IO_SET(port, bits)        port |= (bits)                //set bits on port
#define IO_CLR(port, bits)        port &=~(bits)                //clear bits on port
#define IO_FLP(port, bits)        port ^= (bits)                //flip bits on port

#define IO_GET(port, bits)        ((port) & (bits))        //return bits on port

#define IO_IN(ddr, bits)        ddr |= (bits)                //set bits as input
#define IO_OUT(ddr, bits)                                                //ddr |= (bits)                //set bits as output

#define NOP()                        _nop_()                                        //nop

#ifndef ei
#define ei()                        {EA=1;}                                        //enable interrupt
#endif

#ifndef di
#define di()                        {EA=0;}                                        //disable interrupt
#endif

#define F_CPU                        1000000ul                                //f_cpu runs at 2Mhz
//void (*mcu_reset)(void) = 0x0000;                                 //jump to 0x0000 -> software reset

notice that F_CPU is set to run at 1MIPS.

millwood 发表于 2011-7-12 21:41:50

here is the rtc0.h://using tmr0 as a rtc

//hardware configuration
//end hardware configuration

//predefined intervals for the rtc
#define RTC_ms                                (F_CPU / 1000)                        //1 ms - make sure that it doesn't overflow
#define RTC_10000ms                        (RTC_ms * 10000)                //how many ticks to trigger in 10000ms
#define RTC_5000ms                        (RTC_ms * 5000)                        //how many ticks to trigger in 5000ms
#define RTC_2000ms                        (RTC_ms * 2000)                        //how many ticks to trigger in 2000ms
#define RTC_1000ms                        (RTC_ms * 1000)                        //how many ticks to trigger in 1000ms
#define RTC_500ms                        (RTC_ms * 500)                        //how many ticks to trigger in 500ms
#define RTC_250ms                        (RTC_ms * 250)                        //how many ticks to trigger in 250ms
#define RTC_100ms                        (RTC_ms * 100)                        //how many ticks to trigger in 100ms
//as we use the 16-bit timer, 64ms is the shortest you can go
//as _rtc0_trigger is a long time, the longest period is 4bn ticks -> ~4000 seconds

typedef struct {
        unsigned char half_sec;                                                        //half second indicator - 0=1st half, 1=2nd half
        unsigned char sec;
        unsigned char min;
        unsigned char hour;
        unsigned char day;
} RTC_TIME;

#define RTC0_ERROR                        0                                                //RTC0 error term

extern volatile RTC_TIME _rtc0;

//#define RTC0_TRIGGER                RTC_1000ms                                        //configure rtc0 to trigger
//initialize the timer
//prescaler not used - for compatability reasons only
//8-bit period

void rtc0_init(unsigned long trigger);                                //how frequent is the rtc0_isr to be triggered

//set up the isr handler
void rtc0_act(void (*isr_ptr)(void));                                //set up the isr pointer

void rtc0_update(void);                                                                //update rtc0
here is rtc0.c#include <regx51.h>                                                //we use keil c51
#include "gpio.h"
#include "rtc0.h"                                                //we use tmr0 for rtc

//use black roman's zero cumulative error approach
//this approach will generate an output pulse whose long-term accuracy depends only on the crystal/clock source
//however, it does generate jitter between clocks: the longer the periods, the bigger the jitter
//as such, it is desirable in those applications that require long-term accuracy

//rtc0 error term
//use a positive number if rtc runs too slow;
//use a negative number if rtc runs too fast

//global variable
void (*_rtc0_isr_ptr)(void);                        //function ptr
unsigned long _rtc0_trigger=RTC_1000ms;        //rtc trigger, default to 1s
unsigned long _rtc0_count=0;                        //rtc count: count up to rtc0_trigger
volatile RTC_TIME _rtc0 ={                                //global time keeper for tc0
        0, 58, 59, 23, 0};

void _rtc0_isr(void) interrupt TF0_VECTOR {
        //clear the flag                                        //automatically done by hardware
        _rtc0_count+= 0x10000ul+RTC0_ERROR;        //tmr0 in 16 bit mode
        if (_rtc0_count < _rtc0_trigger) return;        //life goes on
        else {
                _rtc0_count-=_rtc0_trigger;                //reset _rtc0_count;
                rtc0_update();                                        //update rtc0
                _rtc0_isr_ptr();                                //call the handler
        }
}

void rtc0_empty_ptr(void) {                                //empty ptr
}

//initialize the timer
//prescaler not used - for compatability reasons only
//8-bit period
void rtc0_init(unsigned long trigger) {
        TR0=0;                                                                //turn off the timer
        _rtc0_isr_ptr=rtc0_empty_ptr;                //point isr to the empty handler
        _rtc0_count=0;                                                //reset rtc_count
        _rtc0_trigger = trigger;                        //assign the trigger
        TMOD = (TMOD & 0xf0) | 0x01;                //rtc0 in mode 1: 16 bit tmr
        TH0=0;                                                                //set the autoreload period
        TL0=0;                                                                //reset the timer / counter
        ET0=1;                                                                //enable rtc0 interrupt
        TR0=1;                                                                //turn on the timer
}

//set up the isr handler
void rtc0_act(void (*isr_ptr)(void)) {        //set up the isr pointer
        _rtc0_isr_ptr=isr_ptr;
}

void rtc0_update(void) {
        rtc0.half_sec+=1;                                        //increment half_sec indicator
        if (rtc0.half_sec==2) {                                //overflown?
                rtc0.half_sec=0;                                //reset rtc0.half_sec
                rtc0.sec+=1;                                        //increment sec
                if (rtc0.sec==60) {                                //overflown?
                        rtc0.sec=0;                                        //reset rtc0.sec
                        rtc0.min+=1;                                //increment min
                        if (rtc0.min==60) {                        //overflown?
                                rtc0.min=0;                                //reset min
                                rtc0.hour+=1;                        //increment hour
                                if (rtc0.hour==24) {        //overlown?
                                        rtc0.hour=0;                //reset hour
                                        rtc0.day+=1;                //increment day
                                }
                        }
                }
        }
}

millwood 发表于 2011-7-12 21:42:58

here is tmr1 related files.

tmr1.h//initialize the timer
//prescaler not used - for compatability reasons only
//8-bit period

#define TMR1_16BIT                                                //if use 16-bit timer

//predefined intervals for the rtc
#define TMR_ms                                        (F_CPU / 1000)                                //how many ticks to trigger in 1ms
#define TMR_1ms                                        (TMR_ms * 1)                        //how many ticks to trigger in 1ms
#define TMR_2ms                                        (TMR_ms * 2)                        //how many ticks to trigger in 2ms
#define TMR_5ms                                        (TMR_ms * 5)                        //how many ticks to trigger in 5ms
#define TMR_10ms                                (TMR_ms * 10)                        //how many ticks to trigger in 10ms
#define TMR_20ms                                (TMR_ms * 20)                        //how many ticks to trigger in 20ms
#define TMR_50ms                                (TMR_ms * 50)                        //how many ticks to trigger in 50ms

#ifdef TMR1_16BIT
        void tmr1_init(unsigned char prescaler, unsigned short period);
#else
        void tmr1_init(unsigned char prescaler, unsigned char period);
#endif
//set up the isr handler
void tmr1_act(void (*isr_ptr)(void));        //set up the isr pointer
tmr1.c#include <regx51.h>                                                //we use keil c51
#include "gpio.h"
#include "tmr1.h"                                                //we use tmr1

void (*_tmr1_isr_ptr)(void);                        //function ptr
unsigned short _tmr1_period;

void _tmr1_isr(void) interrupt TF1_VECTOR {
        //clear the flag                                        //automatically done by hardware
#ifdef TMR1_16BIT
        TH1+=(_tmr1_period) >> 8;                        //set the autoreload period
        TL1+=_tmr1_period;                                        //reset the timer / counter
#endif
        _tmr1_isr_ptr();                                        //call the handler
}

void tmr1_empty_ptr(void) {                                //empty ptr
}

#ifdef TMR1_16BIT
//initialize the timer
//prescaler not used - for compatability reasons only
//16-bit period
void tmr1_init(unsigned char prescaler, unsigned short period) {
        TR1=0;                                                                //turn off the timer
        _tmr1_isr_ptr=tmr1_empty_ptr;                //point isr to the empty handler
        TMOD = (TMOD & 0x0f) | 0x10;                //tmr1 in mode 1: 16 bit tmr
        _tmr1_period=-period;
        TH1=(_tmr1_period) >> 8;                        //set the autoreload period
        TL1=_tmr1_period;                                        //reset the timer / counter
        ET1=1;                                                                //enable tmr0 interrupt
        TR1=1;                                                                //turn on the timer
}
#else
//initialize the timer
//prescaler not used - for compatability reasons only
//8-bit period
void tmr1_init(unsigned char prescaler, unsigned char period) {
        TR1=0;                                                                //turn off the timer
        _tmr1_isr_ptr=tmr1_empty_ptr;                //point isr to the empty handler
        TMOD = (TMOD & 0x0f) | 0x20;                //tmr0 in mode 2: 16 bit tmr, auto reload TH0
        _tmr1_period=-period;
        TH1=_tmr1_period;                                        //set the autoreload period
        TL1=_0;                                                                //reset the timer / counter
        ET1=1;                                                                //enable tmr0 interrupt
        TR1=1;                                                                //turn on the timer
}
#endif

//set up the isr handler
void tmr1_act(void (*isr_ptr)(void)) {        //set up the isr pointer
        _tmr1_isr_ptr=isr_ptr;
}

millwood 发表于 2011-7-12 21:44:02

here is the 7segment related files:

_7seg.h//_7seg led display driver

//hardware configuration
#define _7SEG_PORT                        P2                                                                //7seg connection, active low
#define _7SEG_DDR                        P2
#define _7SEGs                                0xff
#define _7SEG_OFF(segs)                {_7SEG_PORT= (segs);}                        //turn on segments, active low
#define _7SEG_ON(segs)                {_7SEG_PORT=~(segs);}                        //turn off segments

#define DIG_PORT                        P3                                                                //digits connection, active high
#define DIG_DDR                                P3
#define DIGs                                0xff
#define DIG_ON(digs)                {DIG_PORT= (digs);}                                //turn on digs, active high
#define DIG_OFF(digs)                {DIG_PORT=~(digs);}                                //turn off digs

#define _7SEG_NUMBER                8                                                                //number of digits in the 7segment display
//end hardware configuration

extern unsigned char vRAM;

//initialize the display
void _7seg_init(void);

//display the content of vRAM
void _7seg_display(void);
_7seg.c#include<regx51.h>                                                                                //we use keil c51
#include "gpio.h"
#include "_7seg.h"                                                                                //we use _7seg display

//hardware configuration
//end hardware configuration

//global variable
//display font
unsigned char code _7seg_font[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};//???????

//display buffer
unsigned char vRAM;                                                                        //display buffer

//initialize the display
void _7seg_init(void) {
        _7SEG_OFF(_7SEGs);                                                                        //clear leds - turn off all segments
        IO_OUT(_7SEG_DDR, _7SEGs);                                                        //leds as output

        DIG_OFF(DIGs);                                                                                //all display off
        IO_OUT(DIG_DDR, DIGs);                                                                //all digit pins as output
}

//display the content of vRAM
void _7seg_display(void) {
        static unsigned char digit=0;                                                //digit to be displayed

        DIG_OFF(DIGs);                                                                                //turn all digits off      
        _7SEG_ON(_7seg_font]);                                        //display the digit
        DIG_ON(1<<digit);                                                                        //turn on the digit
        digit+=1;                                                                                        //increment the digit
        if (digit==_7SEG_NUMBER) digit=0;                                        //reset the digit
}

millwood 发表于 2011-7-12 21:50:22

本帖最后由 millwood 于 2011-7-12 21:52 编辑

some notes:

1) you will notice that each .h file has a hardware configuration section. you may need to change them based on your hardware / set-up. for example, the code is now configured to run on a 1MIPS 8051. if you run the code on a 2MIPS 8051, you will need to reconfigure F_CPU to be 2000000ul and recompile the code.

2) the font data is based on your code, which is opposite of what it ought to be. i will change that in the next run.

3) you will also notice that the code is configured to run on a chip with an output directions register. so you can easily port the code to a different mcu (avr, or a pic for example), as long as you provide the relevant .c/.c files to interface with the timers.

finally, here is the connection.

millwood 发表于 2011-7-12 22:16:46

the set-up uses tmr0 as an rtc and tmr1 to drive the display.

but you can easily port rtc0.c/.c files to rtc1.c/.h to use tmr1 as a rtc; and conversely, you can port tmr1.c/.h files to tmr0.c/.h to use tmr0 as a timer.

you can also move the segment pins (P2) / port pins (P3) to a different port - you can simply change the definitions of _7SEG_PORT and DIG_PORT in _7seg.h and recompile.

and the same _7segment code can be used to drive a 2/4/6 digit display: you can simply change the _7SEG_NUMBER definition in _7seg.h file and recompile.

this is how you should code: make your code portable (and well documented) so that the more code you write, the fewer code you write as you can reuse previously written code pieces.

millwood 发表于 2011-7-12 22:27:05

for example, the above _7seg files are written for common anode displays.

to run the code on a common cathode display, you need to make the following changes:#define _7SEG_ON(segs)                {_7SEG_PORT= (segs);}                        //turn on segments, active high
#define _7SEG_OFF(segs)                {_7SEG_PORT=~(segs);}                        //turn off segments

...
#define DIG_OFF(digs)                {DIG_PORT= (digs);}                                //turn off digs, active low
#define DIG_ON(digs)                {DIG_PORT=~(digs);}                                //turn on digs

wey05 发表于 2011-7-12 23:37:37

不错,有点复杂但通用性强,谢谢!

millwood 发表于 2011-7-13 02:37:30

本帖最后由 millwood 于 2011-7-13 02:42 编辑

in the above example, we used a 7segment led display.

what if you want to use a lcd display?

here is the code.#include <regx51.h>                                                        //we use keil c51
#include "gpio.h"
#include "tmr0.h"                                                        //we use tmr0
#include "rtc1.h"                                                        //we use tmr1
#include "_7seg_lcd.h"                                                //7 segment lcd, up to 4 char

//hardware configuration
//end hardware configuration

void rtc2vRAM(void) {
        vRAM=_rtc1.sec %10;                                        //second
        vRAM=_rtc1.sec / 10;

        vRAM=_rtc1.min % 10;                                        //minutes
        vRAM=_rtc1.min / 10;
}


void mcu_init(void) {
}

int main(void) {
        mcu_init();                                                                //reset the mcu

        _7seg_init();                                                        //reset the 7segment display
       
        tmr0_init(1, TMR_10ms);                                        //set tmr0 to trigger periodically
        tmr0_act(_7seg_display);                                //show the display

        rtc1_init(RTC_500ms);                                        //tmr1 to act like a rtc
        rtc1_act(rtc2vRAM);                                                //install rtc timer

        ei();                                                                        //enable global interrupt
        while (1) {
        }
}


look at how similar it is to the led version?

to the application code, it shouldn't matter how the actual display is done - as long as it is done.

that's the beautify of modular programming: you get to benefit from your prior investment in code.

清风车影18 发表于 2011-7-13 19:39:31

目前还在研究中 谢谢!

millwood 发表于 2011-7-14 03:20:44

有点复杂但通用性强

that's the point.

take the rtc module for example. to use it in your code, you need to do the following:

1) include "rtc0.h" in your application code;
2) call rtc0_init() to initialize the rtc so that it triggers periodically.
3) if you want the rtc to perform a job periodically, call rtc0_act() to install your own handler so that it will be called by rtc periodically.

to do that, you don't need to know how rtc0 does its job at all.

the same goes for the tmr or 7segment display modules: they have been purposefully packaged to shield the end users from the inner details.

Paktu 发表于 2011-7-14 10:58:09

如何设置时间?
页: 1 2 3 4 5 6 7 8 9 [10] 11 12 13 14 15 16 17 18
查看完整版本: 帮助新手学习单片机数码管电子钟:原理,c程序详解