这次介绍如何在 uC/OS-II 上实现串口驱动。
/*sci_ucos.h*/
#ifndef _SCI_RTOS_H_
#define _SCI_RTOS_H_
#define SCI_RX_BUF_SIZE 64 /* Number of characters in Rx ring buffer */
#define SCI_TX_BUF_SIZE 64 /* Number of characters in Tx ring buffer */
/*
*********************************************************************************************************
* CONSTANTS
*********************************************************************************************************
*/
#ifndef NUL
#define NUL 0x00
#endif
/* ERROR CODES */
#define SCI_NO_ERR 0 /* Function call was successful */
#define SCI_BAD_CH 1 /* Invalid communications port channel */
#define SCI_RX_EMPTY 2 /* Rx buffer is empty, no character available */
#define SCI_TX_FULL 3 /* Tx buffer is full, could not deposit character */
#define SCI_TX_EMPTY 4 /* If the Tx buffer is empty. */
#define SCI_RX_TIMEOUT 5 /* If a timeout occurred while waiting for a character*/
#define SCI_TX_TIMEOUT 6 /* If a timeout occurred while waiting to send a char.*/
#define SCI_PARITY_NONE 0 /* Defines for setting parity */
#define SCI_PARITY_ODD 1
#define SCI_PARITY_EVEN 2
/*
*********************************************************************************************************
* DATA TYPES
*********************************************************************************************************
*/
typedef struct {
short RingBufRxCtr; /* Number of characters in the Rx ring buffer */
OS_EVENT *RingBufRxSem; /* Pointer to Rx semaphore */
unsigned char *RingBufRxInPtr; /* Pointer to where next character will be inserted */
unsigned char *RingBufRxOutPtr; /* Pointer from where next character will be extracted */
unsigned char RingBufRx[SCI_RX_BUF_SIZE]; /* Ring buffer character storage (Rx) */
short RingBufTxCtr; /* Number of characters in the Tx ring buffer */
OS_EVENT *RingBufTxSem; /* Pointer to Tx semaphore */
unsigned char *RingBufTxInPtr; /* Pointer to where next character will be inserted */
unsigned char *RingBufTxOutPtr; /* Pointer from where next character will be extracted */
unsigned char RingBufTx[SCI_TX_BUF_SIZE]; /* Ring buffer character storage (Tx) */
} SCI_RING_BUF;
/**
* To obtain a character from the communications channel.
* @param port, port can be SCI0 / SCI1
* @param to, is the amount of time (in clock ticks) that the calling function is willing to
* wait for a character to arrive. If you specify a timeout of 0, the function will
* wait forever for a character to arrive.
* @param err, is a pointer to where an error code will be placed:
* *err is set to SCI_NO_ERR if a character has been received
* *err is set to SCI_RX_TIMEOUT if a timeout occurred
* *err is set to SCI_BAD_CH if you specify an invalid channel number
* @return The character in the buffer (or NUL if a timeout occurred)
*/
unsigned char SCIGetCharB (unsigned char ch, unsigned short to, unsigned char *err);
/**
* This function is called by your application to send a character on the communications
* channel. The function will wait for the buffer to empty out if the buffer is full.
* The function returns to your application if the buffer doesn't empty within the specified
* timeout. A timeout value of 0 means that the calling function will wait forever for the
* buffer to empty out. The character to send is first inserted into the Tx buffer and will
* be sent by the Tx ISR. If this is the first character placed into the buffer, the Tx ISR
* will be enabled.
*
* @param port, port can be SCI0 / SCI1
* @param c is the character to send.
* @param to is the timeout (in clock ticks) to wait in case the buffer is full. If you
* specify a timeout of 0, the function will wait forever for the buffer to empty.
* @return SCI_NO_ERR if the character was placed in the Tx buffer
* SCI_TX_TIMEOUT if the buffer didn't empty within the specified timeout period
* SCI_BAD_CH if you specify an invalid channel number
*/
unsigned char SCIPutCharB (unsigned char port, unsigned char c, unsigned short to);
/**
* To initialize the communications module.
* You must call this function before calling any other functions.
*/
void SCIBufferInit (void);
/**
* To see if any character is available from the communications channel.
*
* @param port, port can be SCI0 / SCI1
* @return If at least one character is available, the function returns
* FALSE(0) otherwise, the function returns TRUE(1).
*/
unsigned char SCIBufferIsEmpty (unsigned char port);
/**
* To see if any more characters can be placed in the Tx buffer.
* In other words, this function check to see if the Tx buffer is full.
*
* @param port, port can be SCI0 / SCI1
* @return If the buffer is full, the function returns TRUE
* otherwise, the function returns FALSE.
*/
unsigned char SCIBufferIsFull (unsigned char port);
#endif
/**
* SCI(Serial Communication Interface) Buffered Serial I/O
* @file sci_ucos.c
* @author Li Yuan
* @platform mc9s12XX
* @date 2012-7-22
* @version 1.0.1
*/
#include "derivative.h" /* derivative-specific definitions */
#include <stddef.h>
#include "includes.H"
#include "sci.h"
#include "sci_rtos.h"
/**
* GLOBAL VARIABLES
*/
SCI_RING_BUF SCI0Buf;
SCI_RING_BUF SCI1Buf;
/**
* To obtain a character from the communications channel.
* @param port, port can be SCI0 / SCI1
* @param to, is the amount of time (in clock ticks) that the calling function is willing to
* wait for a character to arrive. If you specify a timeout of 0, the function will
* wait forever for a character to arrive.
* @param err, is a pointer to where an error code will be placed:
* *err is set to SCI_NO_ERR if a character has been received
* *err is set to SCI_RX_TIMEOUT if a timeout occurred
* *err is set to SCI_BAD_CH if you specify an invalid channel number
* @return The character in the buffer (or NUL if a timeout occurred)
*/
unsigned char SCIGetCharB (unsigned char port, unsigned short to, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
unsigned char c;
unsigned char oserr;
SCI_RING_BUF *pbuf;
switch (port)
{ /* Obtain pointer to communications channel */
case SCI0:
pbuf = &SCI0Buf;
break;
case SCI1:
pbuf = &SCI1Buf;
break;
default:
*err = SCI_BAD_CH;
return (0);
}
OSSemPend(pbuf->RingBufRxSem, to, &oserr); /* Wait for character to arrive */
if (oserr == OS_TIMEOUT)
{ /* See if characters received within timeout*/
*err = SCI_RX_TIMEOUT; /* No, return error code */
return (NUL);
}
else
{
OS_ENTER_CRITICAL();
pbuf->RingBufRxCtr--; /* Yes, decrement character count */
c = *pbuf->RingBufRxOutPtr++; /* Get character from buffer */
if (pbuf->RingBufRxOutPtr == &pbuf->RingBufRx[SCI_RX_BUF_SIZE]) { /* Wrap OUT pointer */
pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];
}
OS_EXIT_CRITICAL();
*err = SCI_NO_ERR;
return (c);
}
}
/**
* This function is called by your application to send a character on the communications
* channel. The function will wait for the buffer to empty out if the buffer is full.
* The function returns to your application if the buffer doesn't empty within the specified
* timeout. A timeout value of 0 means that the calling function will wait forever for the
* buffer to empty out. The character to send is first inserted into the Tx buffer and will
* be sent by the Tx ISR. If this is the first character placed into the buffer, the Tx ISR
* will be enabled.
*
* @param port, port can be SCI0 / SCI1
* @param c is the character to send.
* @param to is the timeout (in clock ticks) to wait in case the buffer is full. If you
* specify a timeout of 0, the function will wait forever for the buffer to empty.
* @return SCI_NO_ERR if the character was placed in the Tx buffer
* SCI_TX_TIMEOUT if the buffer didn't empty within the specified timeout period
* SCI_BAD_CH if you specify an invalid channel number
*/
unsigned char SCIPutCharB (unsigned char port, unsigned char c, unsigned short to)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
SCI_RING_BUF *pbuf;
unsigned char oserr;
switch (port)
{ /* Obtain pointer to communications channel */
case SCI0:
pbuf = &SCI0Buf;
break;
case SCI1:
pbuf = &SCI1Buf;
break;
default:
return (SCI_BAD_CH);
}
OSSemPend(pbuf->RingBufTxSem, to, &oserr); /* Wait for space in Tx buffer */
if (oserr == OS_TIMEOUT)
{
return (SCI_TX_TIMEOUT); /* Timed out, return error code */
}
OS_ENTER_CRITICAL();
pbuf->RingBufTxCtr++; /* No, increment character count */
*pbuf->RingBufTxInPtr++ = c; /* Put character into buffer */
if (pbuf->RingBufTxInPtr == &pbuf->RingBufTx[SCI_TX_BUF_SIZE])
{
pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0]; /* Wrap IN pointer */
}
if (pbuf->RingBufTxCtr == 1) /* See if this is the first character */
{
SCIEnableTxInt(port); /* Yes, Enable Tx interrupts */
}
OS_EXIT_CRITICAL();
return (SCI_NO_ERR);
}
/**
* To initialize the communications module.
* You must call this function before calling any other functions.
*/
void SCIBufferInit (void)
{
SCI_RING_BUF *pbuf;
pbuf = &SCI0Buf; /* Initialize the ring buffer for SCI0 */
pbuf->RingBufRxCtr = 0;
pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];
pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];
pbuf->RingBufRxSem = OSSemCreate(0);
pbuf->RingBufTxCtr = 0;
pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0];
pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];
pbuf->RingBufTxSem = OSSemCreate(SCI_TX_BUF_SIZE);
pbuf = &SCI1Buf; /* Initialize the ring buffer for SCI1 */
pbuf->RingBufRxCtr = 0;
pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];
pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];
pbuf->RingBufRxSem = OSSemCreate(0);
pbuf->RingBufTxCtr = 0;
pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0];
pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];
pbuf->RingBufTxSem = OSSemCreate(SCI_TX_BUF_SIZE);
}
/**
* To see if any character is available from the communications channel.
*
* @param port, port can be SCI0 / SCI1
* @return If at least one character is available, the function returns
* FALSE(0) otherwise, the function returns TRUE(1).
*/
unsigned char SCIBufferIsEmpty (unsigned char port)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
unsigned char empty;
SCI_RING_BUF *pbuf;
switch (port)
{ /* Obtain pointer to communications channel */
case SCI0:
pbuf = &SCI0Buf;
break;
case SCI1:
pbuf = &SCI1Buf;
break;
default:
return (0xff);
break;
}
OS_ENTER_CRITICAL();
if (pbuf->RingBufRxCtr > 0)
{ /* See if buffer is empty */
empty = 0; /* Buffer is NOT empty */
}
else
{
empty = 1; /* Buffer is empty */
}
OS_EXIT_CRITICAL();
return (empty);
}
/**
* To see if any more characters can be placed in the Tx buffer.
* In other words, this function check to see if the Tx buffer is full.
*
* @param port, port can be SCI0 / SCI1
* @return If the buffer is full, the function returns TRUE
* otherwise, the function returns FALSE.
*/
unsigned char SCIBufferIsFull (unsigned char port)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
char full;
SCI_RING_BUF *pbuf;
switch (port)
{ /* Obtain pointer to communications channel */
case SCI0:
pbuf = &SCI0Buf;
break;
case SCI1:
pbuf = &SCI1Buf;
break;
default:
return (255);
}
OS_ENTER_CRITICAL();
if (pbuf->RingBufTxCtr < SCI_TX_BUF_SIZE) { /* See if buffer is full */
full = 0; /* Buffer is NOT full */
} else {
full = 1; /* Buffer is full */
}
OS_EXIT_CRITICAL();
return (full);
}
// This function is called by the Rx ISR to insert a character into the receive ring buffer.
static void SCIPutRxChar (unsigned char port, unsigned char c)
{
SCI_RING_BUF *pbuf;
switch (port)
{ /* Obtain pointer to communications channel */
case SCI0:
pbuf = &SCI0Buf;
break;
case SCI1:
pbuf = &SCI1Buf;
break;
default:
return;
}
if (pbuf->RingBufRxCtr < SCI_RX_BUF_SIZE) { /* See if buffer is full */
pbuf->RingBufRxCtr++; /* No, increment character count */
*pbuf->RingBufRxInPtr++ = c; /* Put character into buffer */
if (pbuf->RingBufRxInPtr == &pbuf->RingBufRx[SCI_RX_BUF_SIZE]) { /* Wrap IN pointer */
pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];
}
(void)OSSemPost(pbuf->RingBufRxSem); /* Indicate that character was received */
}
}
// This function is called by the Tx ISR to extract the next character from the Tx buffer.
// The function returns FALSE if the buffer is empty after the character is extracted from
// the buffer. This is done to signal the Tx ISR to disable interrupts because this is the
// last character to send.
static unsigned char SCIGetTxChar (unsigned char port, unsigned char *err)
{
unsigned char c;
SCI_RING_BUF *pbuf;
switch (port)
{ /* Obtain pointer to communications channel */
case SCI0:
pbuf = &SCI0Buf;
break;
case SCI1:
pbuf = &SCI1Buf;
break;
default:
*err = SCI_BAD_CH;
return (0);
}
if (pbuf->RingBufTxCtr > 0) { /* See if buffer is empty */
pbuf->RingBufTxCtr--; /* No, decrement character count */
c = *pbuf->RingBufTxOutPtr++; /* Get character from buffer */
if (pbuf->RingBufTxOutPtr == &pbuf->RingBufTx[SCI_TX_BUF_SIZE]) { /* Wrap OUT pointer */
pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];
}
(void)OSSemPost(pbuf->RingBufTxSem); /* Indicate that character will be sent */
*err = SCI_NO_ERR;
return (c); /* Characters are still available */
} else {
*err = SCI_TX_EMPTY;
return (NUL); /* Buffer is empty */
}
}
void SCI0_ISR_Handler(void)
{
char status;
char data;
unsigned char err;
status = SCI0SR1;
if(status & 0x0F) // 0x1F = 0001 1111, if status is not Receive Data Reg Full Flag
{
// See if we have some kind of error
// Clear interrupt (do nothing about it!)
data = SCI0DRL;
}
else if(status & 0x20) //Receive Data Reg Full Flag
{
data = SCI0DRL;
SCIPutRxChar(SCI0, data); // Insert received character into buffer
}
else if(status & 0x80)
{
data = SCIGetTxChar(SCI0, &err); // Get next character to send.
if (err == SCI_TX_EMPTY)
{ // Do we have anymore characters to send ?
// No, Disable Tx interrupts
SCIDisTxInt(SCI0);
}
else
{
SCI0DRL = data; // Yes, Send character
}
}
}
void SCI1_ISR_Handler (void)
{
char status;
char data;
unsigned char err;
status = SCI1SR1;
if(status & 0x0F) // 0x1F = 0001 1111, if status is not Receive Data Reg Full Flag
{
// See if we have some kind of error
// Clear interrupt (do nothing about it!)
data = SCI1DRL;
}
else if(status & 0x20) //Receive Data Reg Full Flag
{
data = SCI1DRL;
SCIPutRxChar(SCI1, data); // Insert received character into buffer
}
else if(status & 0x80)
{
data = SCIGetTxChar(SCI1, &err); // Get next character to send.
if (err == SCI_TX_EMPTY)
{ // Do we have anymore characters to send ?
// No, Disable Tx interrupts
SCIDisTxInt(SCI1);
}
else
{
SCI1DRL = data; // Yes, Send character
}
}
}
#pragma CODE_SEG NON_BANKED
interrupt VectorNumber_Vsci0 void SCI0_ISR(void)
{
#if defined( __BANKED__) || defined(__LARGE__) || defined(__PPAGE__)
__asm ldaa PPAGE; // 3~, Get current value of PPAGE register
__asm psha; // 2~, Push PPAGE register onto current task's stack
#endif
__asm inc OSIntNesting; //OSIntNesting++;
//if (OSIntNesting == 1)
//{
// OSTCBCur->OSTCBStkPtr = Stack Pointer ;
//}
__asm
{
ldab OSIntNesting
cmpb #$01
bne SCI0ISR1
ldx OSTCBCur
sts 0, x
SCI0ISR1:
}
#if defined( __BANKED__) || defined(__LARGE__) || defined(__PPAGE__)
__asm call SCI0_ISR_Handler;
__asm call OSIntExit;
#else
__asm jsr SCI0_ISR_Handler;
__asm jsr OSIntExit;
#endif
#if defined( __BANKED__) || defined(__LARGE__) || defined(__PPAGE__)
__asm pula; // 3~, Get value of PPAGE register
__asm staa PPAGE; // 3~, Store into CPU's PPAGE register
#endif
}
interrupt VectorNumber_Vsci1 void SCI1_ISR(void)
{
#if defined( __BANKED__) || defined(__LARGE__) || defined(__PPAGE__)
__asm ldaa PPAGE; // 3~, Get current value of PPAGE register
__asm psha; // 2~, Push PPAGE register onto current task's stack
#endif
__asm inc OSIntNesting; //OSIntNesting++;
//if (OSIntNesting == 1)
//{
// OSTCBCur->OSTCBStkPtr = Stack Pointer ;
//}
__asm
{
ldab OSIntNesting
cmpb #$01
bne SCI1ISR1
ldx OSTCBCur
sts 0, x
SCI1ISR1:
}
#if defined( __BANKED__) || defined(__LARGE__) || defined(__PPAGE__)
__asm call SCI1_ISR_Handler;
__asm call OSIntExit;
#else
__asm jsr SCI1_ISR_Handler;
__asm jsr OSIntExit;
#endif
#if defined( __BANKED__) || defined(__LARGE__) || defined(__PPAGE__)
__asm pula; // 3~, Get value of PPAGE register
__asm staa PPAGE; // 3~, Store into CPU's PPAGE register
#endif
}
下面给个简单的例子:
#include <hidef.h> /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */
#include "INCLUDES.H"
#include "crg.h"
#include "sci.h"
#include "sci_rtos.h"
OS_STK AppStartTaskStk[64];
static void AppStartTask (void *pdata);
void main(void)
{
/* put your own code here */
OS_CPU_SR cpu_sr;
CRGInit();
CRGSetRTIFreqency(0x54); // 200Hz
EnableInterrupts;
OS_ENTER_CRITICAL() ;
SCIInit(SCI0) ;
SCIInit(SCI1) ;
OS_EXIT_CRITICAL() ;
OSInit();
SCISetIEBit(SCI0, SCI_RIE) ;
SCISetIEBit(SCI1, SCI_RIE) ;
SCIBufferInit();
(void) OSTaskCreate(AppStartTask, (void *)0x4321, (void *)&AppStartTaskStk[63], 0);
(void)OSStart();
for(;;)
{
_FEED_COP(); /* feeds the dog */
} /* loop forever */
/* please make sure that you never leave main */
}
static void AppStartTask (void *pdata)
{
INT8U err;
char C;
(void) pdata;
for(;;)
{
C = SCIGetCharB(SCI1, 0, &err);
if(err == SCI_NO_ERR)
(void) SCIPutCharB (SCI1, C, 0);
}
}