📄 eval_rtos.c
字号:
/*
**********************************************************************************
*
* Project Name: AT89C5131 MCU and Hi-Tech 8051 compiler evaluation
*
* File Name: eval_rtos.c
*
* Copyright 2004++ by Michael J Bauer [www.hotkey.net.au/~mjbauer/free_ip.htm]
*
* Date Created: 2004.08.26
*
* This source module contains a low-end embedded real-time kernel and I/O device
* drivers comprising a simple RTOS known as "ALERT".
*
* So far as possible, hardware platform dependencies should be confined to this
* module and dedicated device driver modules (e.g. Atmel's USB library files).
*
**********************************************************************************
*/
#include "a89c5131.h" /* MCU-specific definitions */
#include "intrpt.h" /* Compiler-specific interrupt support defs (Hi-Tech C 8051) */
#include "eval_defs.h" /* Eval common definitions (incl. RTOS defs) */
#include "usb_vuart_lib.h" /* Atmel USB library defs */
#include "usb_drv.h"
#include "usb_cdc_enum.h"
const char kacCopyright[] = "ALERT RTOS copyright 2004++ M.J.Bauer ";
/*** globals ***/
idata uint8 gubTaskFlags; /* Background task control/status flags */
idata uint8 gubMiscFlags; /* Misc. system control & status flags */
idata uint16 gwSystemError; /* System Error/Warning flags (16b) */
idata uint8 gbHCI_Stream; /* Determines HCI I/O data stream - USB or UART */
idata struct tDateTime gsRTCbuf; /* global RTC buffer for use by application */
/*** static variables private to this module ***/
static const uint8 kaubDaysinMonth[] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static idata uint8 ubRxCount; /* number of chars in host RX buffer */
static idata char * pcRxTail; /* RX ram buffer pointers */
static idata char * pcRxHead;
static idata char acRxFIFObuf[RXBUFSIZE]; /* UART receive buffer in low RAM */
static idata uint16 uwTickTimer; /* General-purpose ticks timer (unit = 5mS) */
static idata struct tDateTime sRTC; /* "live" RTC date-time registers */
/***
* EEPROM page addresses -- see also _defs.h
*/
far uint8 habEEPROM_page0[128] @ 0x0000;
far uint8 habEEPROM_page1[128] @ 0x0080;
far uint8 habEEPROM_page2[128] @ 0x0100;
far uint8 habEEPROM_page3[128] @ 0x0180;
far uint8 habEEPROM_page4[128] @ 0x0200;
far uint8 habEEPROM_page5[128] @ 0x0280;
far uint8 habEEPROM_page6[128] @ 0x0300;
far uint8 habEEPROM_page7[128] @ 0x0380;
/************************* RTOS Initialisation routine *********************************
*
* Hardware devices and program variables not initialised by the asm startup module
* are initialised here. The application environment and CLI are initialised elsewhere.
*
*/
void
initialise_rtos( void )
{
CKCON0 |= BIT0; /* Set MCU clock 'X2' mode */
IPL0 = 0x12; /* Set IRQ priorities (host UART = highest) */
IPH0 = 0x11;
Set_Bit( LEDCON, BIT3 ); /* Configure P3.3 as LED driver (green?) */
Set_Bit( LEDCON, BIT5 ); /* Configure P3.5 as LED driver (red?) */
#if DEBUG
SetLEDoutput( LED_1, ON ); /*** Switch RED LED ON ***/
for ( uwTickTimer = 0; uwTickTimer < 10000; uwTickTimer++ )
{
; /* Delay to let user see red LED */
}
SetLEDoutput( LED_1, OFF ); /*** Switch RED LED OFF ***/
#endif
#if WATCHDOG_ENABLED
WDTPRG = 2; /* Set WDT prescaler */
service_watchdog(); /* Activate the 8051 on-chip watchdog timer */
#endif
initialise_RTI_timer();
initialise_uart();
gsRTCbuf.year = 0; /* Set RTC to "relative time" until set correctly */
gsRTCbuf.month = 1;
gsRTCbuf.day = 1;
gsRTCbuf.dow = 6;
gsRTCbuf.hour = 0;
gsRTCbuf.min = 0;
gsRTCbuf.sec = 0;
set_RTC_date_time(); /* Default to Saturday 01/01/2000 00:00:00 */
Set_Bit( gwSystemError, RTC_INVALID );
ei(); /* Launch foreground -- Enable global interrupts */
}
/*****
* Watchdog service routine. Enables and resets the watchdog timer.
* Called frequently from periodic background task, at intervals not exceeding 10 mSec.
* If the background process hangs somewhere, or if the RTI tick interrupt fails,
* the watchdog will cause an MCU reset.
*/
void
service_watchdog( void )
{
WDTRST = 0x1E; /* Reset the watchdog timer */
WDTRST = 0xE1;
}
/*****
* RTI timer (8051 Timer T0) initialisation.
* Timer T0 is setup to generate a periodic interrupt. Tick interval = 5 mSec.
*
* Timer clock freq (X1 mode), Fclk = Fosc/12 = 16/12 MHz = 1.333MHz.
* Timer clock period (X1 mode), Tclk = 12/16 uS = 0.75uS.
* Req'd RTI tick interval = 5mS = 5000uS/0.75 clocks = 6666 clocks => CLOCKS_PER_TICK.
* (This number may need tweaking to compensate for Timer ISR processing overheads.)
*
* Called by: initialise_rtos()
* Entry args: --
* Returns: --
* Affects: --
*
*/
void
initialise_RTI_timer( void )
{
/* Install vector for Timer 0 interrupt service */
ROM_VECTOR( 0x0B, Timer0_service );
TCON = 0x00; /* Stop the timer */
TMOD = 0x01; /* Set T0 to mode 1: 16-bit timer */
TH0 = HI_BYTE( 65535 - CLOCKS_PER_TICK ); /* Load timer registers */
TL0 = LO_BYTE( 65535 - CLOCKS_PER_TICK );
Set_Bit( IEN0, BIT1 ); /* Enable Timer 0 interrupt */
Set_Bit( TCON, BIT4 ); /* Start the timer */
}
/*
* 8051 timer T0 service function -- calls the RTI "tick" handler.
* Timer T0 is configured to interrupt every 5 milli-seconds;
* (see "initialise_RTI_timer()")
* Timer T0 overflow/INT flag (TCON<TF0>) is cleared automatically on vectoring to this ISR.
*/
interrupt
Timer0_service( void )
{
Clear_Bit( TCON, BIT4 ); /* Stop the timer */
TH0 = HI_BYTE( 65535 - CLOCKS_PER_TICK ); /* Reload timer registers */
TL0 = LO_BYTE( 65535 - CLOCKS_PER_TICK );
Set_Bit( TCON, BIT4 ); /* Restart the timer */
RTI_Tick_Handler();
}
/*****
* RTI_Tick_Handler
*
* Function is called every 5 milliSec (=tick) from the Timer 0 interrupt service routine.
* The real-time clock/calendar is maintained here.
* Periodic background tasks are scheduled here.
* Any other *brief* time-critical foreground tasks may be called from this function.
*/
void
RTI_Tick_Handler( void )
{
static idata uint8 ubTicks;
uint8 ubDaysintheMonth;
bool yLeapYear;
bool yRollSec = FALSE;
bool yRollDay = FALSE;
uwTickTimer++ ;
if ( ++ubTicks >= 200 ) /* This happens every second */
{
ubTicks = 0;
yRollSec = TRUE;
if ( ++sRTC.sec >= 60 )
{
sRTC.sec = 0;
if ( ++sRTC.min >= 60 )
{
sRTC.min = 0;
if ( ++sRTC.hour >= 24 )
{
sRTC.hour = 0;
yRollDay = TRUE;
}
}
}
}
if ( yRollDay ) /* This happens every day at midnight */
{
sRTC.dow++ ;
if ( sRTC.dow >= 7 ) sRTC.dow = 0; /* it must be Sunday */
yLeapYear = ( sRTC.year % 4 == 0 && sRTC.year != 0 );
if ( yLeapYear && sRTC.month == 2 )
ubDaysintheMonth = 29;
else
ubDaysintheMonth = kaubDaysinMonth[sRTC.month];
if ( ++sRTC.day > ubDaysintheMonth )
{
sRTC.day = 1;
if ( ++sRTC.month > 12 )
{
sRTC.month = 1;
if ( ++sRTC.year > 99 ) /* year is 2099 ! */
sRTC.year = 0;
}
}
}
if ( Test_Bit( gubTaskFlags, TICKS_TASK ) )
Set_Bit( gwSystemError, TICK_TASK_OVRUN );
Set_Bit( gubTaskFlags, TICKS_TASK ); /* Invoke this background task every tick */
if ( (ubTicks % 10) == 0 )
{
if ( Test_Bit( gubTaskFlags, FIFTY_MSEC_TASK ) )
Set_Bit( gwSystemError, FIFTYMS_TASK_OVRUN );
Set_Bit( gubTaskFlags, FIFTY_MSEC_TASK ); /* Invoke this task every 50mSec (10 ticks) */
}
if ( yRollSec )
{
if ( Test_Bit( gubTaskFlags, SECONDS_TASK ) )
Set_Bit( gwSystemError, SEC_TASK_OVRUN );
Set_Bit( gubTaskFlags, SECONDS_TASK ); /* Invoke this B/G task every second */
}
if ( yRollDay )
{
if ( Test_Bit( gubTaskFlags, NEWDAY_TASK ) )
Set_Bit( gwSystemError, NEWDAY_TASK_OVRUN );
Set_Bit( gubTaskFlags, NEWDAY_TASK ); /* Invoke this B/G task every day at 00:00 */
}
LED_Refresh();
}
/*
* get_ticks() is a general-purpose timing function.
* It returns the value of a continuous free-running 16-bit timer "register", uwTickTimer,
* which is incremented every tick (5mS) by the RTI timer service routine.
* Because uwTickTimer is a 2-byte variable, RTI timer interrupts must be disabled while
* the variable is being read.
*/
uint16
get_ticks( void )
{
uint16 uwTicksNow;
uint8 bIntStatus = IEN0; // Save INT status register
Clear_Bit( IEN0, BIT7 ); // Disable global INT
uwTicksNow = uwTickTimer; // Capture uwTickTimer
IEN0 = bIntStatus; // Restore INT status register
return uwTicksNow;
}
/*
* Waits for a specified number of timer ticks (=arg1).
* Background tasks will be executed as usual while waiting.
* This function cannot be called from within the background process.
*
* Called by: Command functions
* Entry args: (uint16) uwDuration = time delay (ticks = mS x 5)
* Returns: void
* Affects: uwTickTimer (decremented by timer "tick" service routine)
*/
void
wait_ticks( uint16 uwDuration )
{
uint16 wStartTime = get_ticks();
while ( get_ticks() - wStartTime < uwDuration )
{
background_process();
}
}
/************************* REAL-TIME CLOCK FUNCTIONS ******************************
*
* Set the real-time clock registers from date/time values stored in the global
* RTC buffer: gsRTCbuf, which must be setup before the call.
* The process is repeated if the "live" RTC 'seconds' register rolls over
* during the process, to ensure that the live data are not currupted by a timer
* tick interrupt. One re-try should suffice, but the function will loop until
* doomsday (i.e. until watchdog reset occurs) if necessary.
*
* The day-of-the-week value is computed from the set date and time, based on 01/01/2000
* being a Saturday (dow = 6) and the number of days passed since that day.
*/
void
set_RTC_date_time( void )
{
uint8 bYear;
uint16 wDaysPassed = 0; /* Days passed since Saturday 01/01/2000 */
do
{
sRTC.sec = gsRTCbuf.sec;
sRTC.min = gsRTCbuf.min;
sRTC.hour = gsRTCbuf.hour;
sRTC.day = gsRTCbuf.day;
sRTC.month = gsRTCbuf.month;
sRTC.year = gsRTCbuf.year;
}
while ( sRTC.sec != gsRTCbuf.sec );
for ( bYear=0; bYear < gsRTCbuf.year; bYear++ )
{
wDaysPassed += days_in_whole_year( bYear );
}
wDaysPassed += day_of_this_year() - 1;
sRTC.dow = (wDaysPassed + 6) % 7 ; /*** DOW: 0==SUN, 1==MON, ... 6==SAT ***/
}
/*
* Read the "live" real-time clock registers into the global RTC buffer: gsRTCbuf.
* The process is repeated if the "live" RTC seconds register rolls over
* during the process, to ensure that the buffer's data are not currupted
* by a timer tick interrupt. One re-try should suffice.
* The buffered RTC date/time is checked for validity; if invalid,
* the function returns FALSE, otherwise TRUE.
*/
bool
read_RTC_date_time( void )
{
do
{
gsRTCbuf.sec = sRTC.sec;
gsRTCbuf.min = sRTC.min;
gsRTCbuf.hour = sRTC.hour;
gsRTCbuf.dow = sRTC.dow;
gsRTCbuf.day = sRTC.day;
gsRTCbuf.month = sRTC.month;
gsRTCbuf.year = sRTC.year;
}
while ( sRTC.sec != gsRTCbuf.sec );
if ( ( gsRTCbuf.month > 12 )
|| ( gsRTCbuf.month == 0 )
|| ( gsRTCbuf.day > 31 )
|| ( gsRTCbuf.day == 0 )
|| ( gsRTCbuf.hour > 23 )
|| ( gsRTCbuf.min > 59 )
|| ( gsRTCbuf.sec > 59 ) )
{
Set_Bit( gwSystemError, RTC_INVALID );
return FALSE;
}
else return TRUE;
}
/*****
* Compute number of days in whole of year specified.
*
* Arg(s): (uint8) bYear = year in the range 0..99 (0 => 2000)
* Returns: 366 if bYear is a leap year, else 365.
*/
uint16
days_in_whole_year( uint8 bYear )
{
if ( bYear % 4 == 0 ) return 366; /* It's a leap year */
else return 365;
}
/*****
* Compute the 'day-of-year" number (1..366) for the date stored in the global date.time
* structure, gsRTCbuf.
*
* Returns: Day-of-year, in the range 1..365, (1..366 if it's a leap year).
*/
uint16
day_of_this_year( void )
{
uint8 bMonth;
uint16 wResult = gsRTCbuf.day;
bool yLeapYear = ( gsRTCbuf.year % 4 == 0 );
for ( bMonth = 1; bMonth < gsRTCbuf.month; bMonth++ )
{
if ( yLeapYear && bMonth == 2 ) wResult += 29;
else wResult += kaubDaysinMonth[bMonth];
}
return wResult;
}
/**************************** UART I/O FUNCTIONS **********************************
*
* Async serial comms interface support routines (to suit AT89C5131 on-chip UART).
* This firmware uses interrupt mode for received data only.
* The UART provides a command/response comms link to the Host PC (or user terminal).
*
* The internal Baud-rate register value (BRL) is selected by the formula:
* BRL = 256 - BDCLKDIV, where BDCLKDIV is the Baudrate clock divisor value...
* BDCLKDIV = (Fosc / 2) / (16 * BAUD), where BAUD is the required Baud rate,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -