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
如何设置时间?