您的位置:首页 > 编程语言 > C语言/C++

[嵌入式开发模块]MC9S12XEP100 SCI(UART)驱动程序2 - 基于uCOS-II

2017-06-24 11:58 316 查看
忙着毕设,很久没有写文章了,终于答辩完了,得了个校优秀毕业设计。毕设做的是个智能接口模块,用一周时间入门了MC9S12XEP100的开发,又用一周时间入门了uC/OS-II嵌入式操作系统,在做毕设的过程中学到了很多,现在把一些工具发上来分享。

首先先发一个自己封装的MC9S12XEP100的SCI模块(也就是UART模块)的驱动。这些代码参考了 Li Yuan http://blog.csdn.net/liyuanbhu/article/details/7764851 的代码,整个代码风格是按照uCOS-II操作系统源码的风格来写的,在此表示感谢。

目前还不是特别完整完善,但基本使用是没有问题了。

上一章中已经发了硬件驱动部分,这里发下基于uCOS-II嵌入式操作系统的驱动部分以及顺便说下怎么用。

首先要记得到上一章中把两个文件存了,顺便还要把.h文件中的

SCI_UCOS_MODULE_ENABLE 后面改为 TRUE 以开启RTOS(嵌入式操作系统)驱动部分。

目前这个版本已经支持所有8个SCI口了。

首先是按照uCOS-II操作系统的要求写的中断服务程序。

;********************************************************************************************************
;                                               uC/OS-II
;                                         The Real-Time Kernel
;
;                         (c) Copyright 2002, Jean J. Labrosse, Weston, FL
;                                          All Rights Reserved
;
;
;                                       PAGED S12X Specific code
;                                            (CODEWARRIOR)
;
; File         : SCI_uCos.s
; By           : Lin Shijun(http://blog.csdn.net/lin_strong)
;
; Notes        : THIS FILE *MUST* BE LINKED INTO NON_BANKED MEMORY! 这个文件必须放在非分页内存中
;                    modified according to uC/OS-II's example.  依据uC/OS-II的模版修改。
;********************************************************************************************************

NON_BANKED:       section

;********************************************************************************************************
;                                           I/O PORT ADDRESSES   I/O口地址
;********************************************************************************************************

PPAGE:            equ    $0015         ; Addres of PPAGE register (assuming MC9S12XEP100 part)
RPAGE:            equ    $0016         ; Addres of RPAGE register (assuming MC9S12XEP100 part)
EPAGE:            equ    $0017         ; Addres of EPAGE register (assuming MC9S12XEP100 part)
GPAGE:            equ    $0010         ; Addres of GPAGE register (assuming MC9S12XEP100 part)

;********************************************************************************************************
;                                          PUBLIC DECLARATIONS   公开声明
;********************************************************************************************************

xdef   SCI0_RxTxISR
xdef   SCI1_RxTxISR
xdef   SCI2_RxTxISR
xdef   SCI3_RxTxISR
xdef   SCI4_RxTxISR
xdef   SCI5_RxTxISR
xdef   SCI6_RxTxISR
xdef   SCI7_RxTxISR

;********************************************************************************************************
;                                         EXTERNAL DECLARATIONS  外部声明
;****************************************************************************
1e92c
****************************

xref   OSIntExit
xref   OSIntNesting
xref   OSTCBCur

xref   SCI0_ISR_Handler
xref   SCI1_ISR_Handler
xref   SCI2_ISR_Handler
xref   SCI3_ISR_Handler
xref   SCI4_ISR_Handler
xref   SCI5_ISR_Handler
xref   SCI6_ISR_Handler
xref   SCI7_ISR_Handler

;********************************************************************************************************
;                                           SCI RxTx ISR
;
; Description : This routine is the uC/Probe RxTx interrupt service routine
;
; Arguments   : none
;
; Notes       : 1) All USER interrupts should be modeled EXACTLY like this where the only
;                  line to be modified is the call to your ISR_Handler and perhaps the call to
;                  the label name SCI0_ISR_Handler.
;********************************************************************************************************

SCI0_RxTxISR:
ldaa   GPAGE                       ; Get current value of GPAGE register
psha                               ; Push GPAGE register onto current task's stack

ldaa   EPAGE                       ; Get current value of EPAGE register
psha                               ; Push EPAGE register onto current task's stack

ldaa   RPAGE                       ; Get current value of RPAGE register
psha                               ; Push RPAGE register onto current task's stack

ldaa   PPAGE                       ; Get current value of PPAGE register
psha                               ; Push PPAGE register onto current task's stack

inc    OSIntNesting                ; Notify uC/OS-II about ISR

ldab   OSIntNesting                ; if (OSIntNesting == 1) {
cmpb   #$01
bne    SCI0_RxTxISR1

ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer
sts    0,y                         ; }

SCI0_RxTxISR1:
call   SCI0_ISR_Handler  ; Call TxRx ISR handler. (See SCI_uCos.c)

;   cli                               ; Optionally enable interrupts to allow interrupt nesting

call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().

pula                               ; Get value of PPAGE register
staa   PPAGE                       ; Store into CPU's PPAGE register

pula                               ; Get value of RPAGE register
staa   RPAGE                       ; Store into CPU's RPAGE register

pula                               ; Get value of EPAGE register
staa   EPAGE                       ; Store into CPU's EPAGE register

pula                               ; Get value of GPAGE register
staa   GPAGE                       ; Store into CPU's GPAGE register

rti                               ; Return from interrupt to interrupted task.

SCI1_RxTxISR:
ldaa   GPAGE                       ; Get current value of GPAGE register
psha                               ; Push GPAGE register onto current task's stack

ldaa   EPAGE                       ; Get current value of EPAGE register
psha                               ; Push EPAGE register onto current task's stack

ldaa   RPAGE                       ; Get current value of RPAGE register
psha                               ; Push RPAGE register onto current task's stack

ldaa   PPAGE                       ; Get current value of PPAGE register
psha                               ; Push PPAGE register onto current task's stack

inc    OSIntNesting                ; Notify uC/OS-II about ISR

ldab   OSIntNesting                ; if (OSIntNesting == 1) {
cmpb   #$01
bne    SCI1_RxTxISR1

ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer
sts    0,y                         ; }

SCI1_RxTxISR1:
call   SCI1_ISR_Handler  ; Call TxRx ISR handler. (See SCI_uCos.c)

;   cli                               ; Optionally enable interrupts to allow interrupt nesting

call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().

pula                               ; Get value of PPAGE register
staa   PPAGE                       ; Store into CPU's PPAGE register

pula                               ; Get value of RPAGE register
staa   RPAGE                       ; Store into CPU's RPAGE register

pula                               ; Get value of EPAGE register
staa   EPAGE                       ; Store into CPU's EPAGE register

pula                               ; Get value of GPAGE register
staa   GPAGE                       ; Store into CPU's GPAGE register

rti                               ; Return from interrupt to interrupted task.

SCI2_RxTxISR:
ldaa   GPAGE                       ; Get current value of GPAGE register
psha                               ; Push GPAGE register onto current task's stack

ldaa   EPAGE                       ; Get current value of EPAGE register
psha                               ; Push EPAGE register onto current task's stack

ldaa   RPAGE                       ; Get current value of RPAGE register
psha                               ; Push RPAGE register onto current task's stack

ldaa   PPAGE                       ; Get current value of PPAGE register
psha                               ; Push PPAGE register onto current task's stack

inc    OSIntNesting                ; Notify uC/OS-II about ISR

ldab   OSIntNesting                ; if (OSIntNesting == 1) {
cmpb   #$01
bne    SCI2_RxTxISR1

ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer
sts    0,y                         ; }

SCI2_RxTxISR1:
call   SCI2_ISR_Handler  ; Call TxRx ISR handler. (See SCI_uCos.c)

;   cli                               ; Optionally enable interrupts to allow interrupt nesting

call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().

pula                               ; Get value of PPAGE register
staa   PPAGE                       ; Store into CPU's PPAGE register

pula                               ; Get value of RPAGE register
staa   RPAGE                       ; Store into CPU's RPAGE register

pula                               ; Get value of EPAGE register
staa   EPAGE                       ; Store into CPU's EPAGE register

pula                               ; Get value of GPAGE register
staa   GPAGE                       ; Store into CPU's GPAGE register

rti                               ; Return from interrupt to interrupted task.

SCI3_RxTxISR:
ldaa   GPAGE                       ; Get current value of GPAGE register
psha                               ; Push GPAGE register onto current task's stack

ldaa   EPAGE                       ; Get current value of EPAGE register
psha                               ; Push EPAGE register onto current task's stack

ldaa   RPAGE                       ; Get current value of RPAGE register
psha                               ; Push RPAGE register onto current task's stack

ldaa   PPAGE                       ; Get current value of PPAGE register
psha                               ; Push PPAGE register onto current task's stack

inc    OSIntNesting                ; Notify uC/OS-II about ISR

ldab   OSIntNesting                ; if (OSIntNesting == 1) {
cmpb   #$01
bne    SCI3_RxTxISR1

ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer
sts    0,y                         ; }

SCI3_RxTxISR1:
call   SCI3_ISR_Handler  ; Call TxRx ISR handler. (See SCI_uCos.c)

;   cli                               ; Optionally enable interrupts to allow interrupt nesting

call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().

pula                               ; Get value of PPAGE register
staa   PPAGE                       ; Store into CPU's PPAGE register

pula                               ; Get value of RPAGE register
staa   RPAGE                       ; Store into CPU's RPAGE register

pula                               ; Get value of EPAGE register
staa   EPAGE                       ; Store into CPU's EPAGE register

pula                               ; Get value of GPAGE register
staa   GPAGE                       ; Store into CPU's GPAGE register

rti                               ; Return from interrupt to interrupted task.

SCI4_RxTxISR:
ldaa   GPAGE                       ; Get current value of GPAGE register
psha                               ; Push GPAGE register onto current task's stack

ldaa   EPAGE                       ; Get current value of EPAGE register
psha                               ; Push EPAGE register onto current task's stack

ldaa   RPAGE                       ; Get current value of RPAGE register
psha                               ; Push RPAGE register onto current task's stack

ldaa   PPAGE                       ; Get current value of PPAGE register
psha                               ; Push PPAGE register onto current task's stack

inc    OSIntNesting                ; Notify uC/OS-II about ISR

ldab   OSIntNesting                ; if (OSIntNesting == 1) {
cmpb   #$01
bne    SCI4_RxTxISR1

ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer
sts    0,y                         ; }

SCI4_RxTxISR1:
call   SCI4_ISR_Handler  ; Call TxRx ISR handler. (See SCI_uCos.c)

;   cli                               ; Optionally enable interrupts to allow interrupt nesting

call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().

pula                               ; Get value of PPAGE register
staa   PPAGE                       ; Store into CPU's PPAGE register

pula                               ; Get value of RPAGE register
staa   RPAGE                       ; Store into CPU's RPAGE register

pula                               ; Get value of EPAGE register
staa   EPAGE                       ; Store into CPU's EPAGE register

pula                               ; Get value of GPAGE register
staa   GPAGE                       ; Store into CPU's GPAGE register

rti                               ; Return from interrupt to interrupted task.

SCI5_RxTxISR:
ldaa   GPAGE                       ; Get current value of GPAGE register
psha                               ; Push GPAGE register onto current task's stack

ldaa   EPAGE                       ; Get current value of EPAGE register
psha                               ; Push EPAGE register onto current task's stack

ldaa   RPAGE                       ; Get current value of RPAGE register
psha                               ; Push RPAGE register onto current task's stack

ldaa   PPAGE                       ; Get current value of PPAGE register
psha                               ; Push PPAGE register onto current task's stack

inc    OSIntNesting                ; Notify uC/OS-II about ISR

ldab   OSIntNesting                ; if (OSIntNesting == 1) {
cmpb   #$01
bne    SCI5_RxTxISR1

ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer
sts    0,y                         ; }

SCI5_RxTxISR1:
call   SCI5_ISR_Handler  ; Call TxRx ISR handler. (See SCI_uCos.c)

;   cli                               ; Optionally enable interrupts to allow interrupt nesting

call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().

pula                               ; Get value of PPAGE register
staa   PPAGE                       ; Store into CPU's PPAGE register

pula                               ; Get value of RPAGE register
staa   RPAGE                       ; Store into CPU's RPAGE register

pula                               ; Get value of EPAGE register
staa   EPAGE                       ; Store into CPU's EPAGE register

pula                               ; Get value of GPAGE register
staa   GPAGE                       ; Store into CPU's GPAGE register

rti                               ; Return from interrupt to interrupted task.

SCI6_RxTxISR:
ldaa   GPAGE                       ; Get current value of GPAGE register
psha                               ; Push GPAGE register onto current task's stack

ldaa   EPAGE                       ; Get current value of EPAGE register
psha                               ; Push EPAGE register onto current task's stack

ldaa   RPAGE                       ; Get current value of RPAGE register
psha                               ; Push RPAGE register onto current task's stack

ldaa   PPAGE                       ; Get current value of PPAGE register
psha                               ; Push PPAGE register onto current task's stack

inc    OSIntNesting                ; Notify uC/OS-II about ISR

ldab   OSIntNesting                ; if (OSIntNesting == 1) {
cmpb   #$01
bne    SCI6_RxTxISR1

ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer
sts    0,y                         ; }

SCI6_RxTxISR1:
call   SCI6_ISR_Handler  ; Call TxRx ISR handler. (See SCI_uCos.c)

;   cli                               ; Optionally enable interrupts to allow interrupt nesting

call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().

pula                               ; Get value of PPAGE register
staa   PPAGE                       ; Store into CPU's PPAGE register

pula                               ; Get value of RPAGE register
staa   RPAGE                       ; Store into CPU's RPAGE register

pula                               ; Get value of EPAGE register
staa   EPAGE                       ; Store into CPU's EPAGE register

pula                               ; Get value of GPAGE register
staa   GPAGE                       ; Store into CPU's GPAGE register

rti                               ; Return from interrupt to interrupted task.

SCI7_RxTxISR:
ldaa   GPAGE                       ; Get current value of GPAGE register
psha                               ; Push GPAGE register onto current task's stack

ldaa   EPAGE                       ; Get current value of EPAGE register
psha                               ; Push EPAGE register onto current task's stack

ldaa   RPAGE                       ; Get current value of RPAGE register
psha                               ; Push RPAGE register onto current task's stack

ldaa   PPAGE                       ; Get current value of PPAGE register
psha                               ; Push PPAGE register onto current task's stack

inc    OSIntNesting                ; Notify uC/OS-II about ISR

ldab   OSIntNesting                ; if (OSIntNesting == 1) {
cmpb   #$01
bne    SCI7_RxTxISR1

ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer
sts    0,y                         ; }

SCI7_RxTxISR1:
call   SCI7_ISR_Handler  ; Call TxRx ISR handler. (See SCI_uCos.c)

;   cli                               ; Optionally enable interrupts to allow interrupt nesting

call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().

pula                               ; Get value of PPAGE register
staa   PPAGE                       ; Store into CPU's PPAGE register

pula                               ; Get value of RPAGE register
staa   RPAGE                       ; Store into CPU's RPAGE register

pula                               ; Get value of EPAGE register
staa   EPAGE                       ; Store into CPU's EPAGE register

pula                               ; Get value of GPAGE register
staa   GPAGE                       ; Store into CPU's GPAGE register

rti                               ; Return from interrupt to interrupted task.


要记得修改中断向量表把用到的端口的ISR分别指向其中断地址,如SCI0_RxTxISR指向0xFFD6、SCI1_RxTxISR1指向0xFFD4,也就是SCI0和SCI1的中断地址。

然后是.c文件:

/*
*******************************************************************************************
*
*
*                    SCI(Serial Communication Interface)  Buffered Serial I/O
*                                   Freescale MC9S12XEP100
*                           飞思卡尔   MC9S12XEP100  SCI支持包
*
* File : SCI_uCos.c
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2018/02/23
* version: V2.2
* History: 2017/04/24  V1.0   the prototype of SCI_uCOS,only support SCI0 and SCI1
*          2017/07/20  V2.0   update the module to support the SCI0 to SCI7
*          2017/10/27  V2.1   fix a bug: when disable a port between two active port,the upper one can't work.
*                             this is due to the statment:
*                                  for(port = 0;port < 8 && isSCIEnable(port);port++)
*                             in SCI_BufferInit().
*                             the judgment statment port < 8 && isSCIEnable(port) will jump out the init loop
*                               the first time meet the first disabled SCI port.
*          2018/02/23  V2.2  a. optimize the struct referencing to the register
*                            b. delete some configuration marco, because the unused function will not be linked
*                               in the project, so there is no need to hide the function.
*                            c. add more error check.
*                            d. add a marco(SCI_ARGUMENT_CHECK_EN) to turn off argument check for saving code.
*                            e. add the support for define buffer in paged addressing area.
*                            f. use semaphore in replace of the origional mutex, so the user don't need to take
*                               care of the priority thing. The cost is the possibility of priority inversion.
*                               But I think it's worth it.
* NOTE(s): 1.refer to Li Yuan's  (http://blog.csdn.net/liyuanbhu/article/details/7768914)
*             and
*            uCOS-II's Code
*          2.记得把SCI_uCOS.s中的SCIx_RxTxISR中断服务程序指向对应的中断向量地址
*          3.如果你想要把缓冲区定义到分页区或者全局地址的话,参考第210行。
********************************************************************************************
*/

/*
********************************************************************************************
*                                   INCLUDES
********************************************************************************************
*/
#include "SCI_def.h"
#if(SCI_MODULE_ENABLE == TRUE && SCI_UCOS_MODULE_ENABLE == TRUE)

#include <string.h>
#include "RingQueue.h"

/*
*********************************************************************************************
*                                    DATA TYPES
*********************************************************************************************
*/

typedef struct{
INT8U port;                                 /* Hold the port number         */
OS_EVENT  *RingBufRxSem;                    /* Pointer to Rx semaphore      */
RING_QUEUE*  RingBufRx;                     /* Ring buffer character storage (Rx) */
OS_EVENT  *RingBufTxMutex;                  /* Pointer to Tx MUTUAL semaphore     */
OS_EVENT  *RingBufTxSem;                    /* Pointer to Tx semaphore          */
RING_QUEUE*  RingBufTx;                     /* Ring buffer character storage (Tx) */
} SCI_RING_BUF, *pSCI_RING_BUF;

/*
****************************************************************************************
*                                 CONSTANT
****************************************************************************************
*/
// Flag of SCI enable.
#define EnableFlag ((SCI0_UCOS_CODE_EN << 0) | (SCI1_UCOS_CODE_EN << 1) | (SCI2_UCOS_CODE_EN << 2) | \
(SCI3_UCOS_CODE_EN << 3) | (SCI4_UCOS_CODE_EN << 4) | (SCI5_UCOS_CODE_EN << 5) | \
(SCI6_UCOS_CODE_EN << 6) | (SCI7_UCOS_CODE_EN << 7))
#define isSCIEnable(port) ((EnableFlag >> port) & 0x01)

// const of RxBuffer Sizes
#if(SCI0_RX_BUFFER_SIZE < 256 && SCI1_RX_BUFFER_SIZE < 256 && SCI2_RX_BUFFER_SIZE < 256 && \
SCI3_RX_BUFFER_SIZE < 256 && SCI4_RX_BUFFER_SIZE < 256 && SCI5_RX_BUFFER_SIZE < 256 && \
SCI6_RX_BUFFER_SIZE < 256 && SCI7_RX_BUFFER_SIZE < 256)
#define RxSizeArrType INT8U
#else
#define RxSizeArrType INT16U
#endif

static const RxSizeArrType SCIRxBufferSize[]={
SCI0_RX_BUFFER_SIZE,
SCI1_RX_BUFFER_SIZE,
SCI2_RX_BUFFER_SIZE,
SCI3_RX_BUFFER_SIZE,
SCI4_RX_BUFFER_SIZE,
SCI5_RX_BUFFER_SIZE,
SCI6_RX_BUFFER_SIZE,
SCI7_RX_BUFFER_SIZE,
};
#define SCIRxBufSize(port) SCIRxBufferSize[port]
#define isSCIRxEnable(port) (SCIRxBufferSize[port] > 0)

// const of TxBuffer Sizes
#if(SCI0_TX_BUFFER_SIZE < 256 && SCI1_TX_BUFFER_SIZE < 256 && SCI2_TX_BUFFER_SIZE < 256 && SCI3_TX_BUFFER_SIZE < 256 \
&& SCI4_TX_BUFFER_SIZE < 256 && SCI5_TX_BUFFER_SIZE < 256 && SCI6_TX_BUFFER_SIZE < 256 && SCI7_TX_BUFFER_SIZE < 256)
#define TxSizeArrType INT8U
#else
#define TxSizeArrType INT16U
#endif

static const TxSizeArrType SCITxBufferSize[]={
SCI0_TX_BUFFER_SIZE,
SCI1_TX_BUFFER_SIZE,
SCI2_TX_BUFFER_SIZE,
SCI3_TX_BUFFER_SIZE,
SCI4_TX_BUFFER_SIZE,
SCI5_TX_BUFFER_SIZE,
SCI6_TX_BUFFER_SIZE,
SCI7_TX_BUFFER_SIZE,
};
#define SCITxBufSize(port) SCITxBufferSize[port]
#define isSCITxEnable(port) (SCITxBufferSize[port] > 0)

#define SCI_RX_BUFFER_SIZE_SUM (SCI0_RX_BUFFER_SIZE + SCI1_RX_BUFFER_SIZE + SCI2_RX_BUFFER_SIZE + SCI3_RX_BUFFER_SIZE \
+ SCI4_RX_BUFFER_SIZE + SCI5_RX_BUFFER_SIZE + SCI6_RX_BUFFER_SIZE + SCI7_RX_BUFFER_SIZE)
#define SCI_TX_BUFFER_SIZE_SUM (SCI0_TX_BUFFER_SIZE + SCI1_TX_BUFFER_SIZE + SCI2_TX_BUFFER_SIZE + SCI3_TX_BUFFER_SIZE \
+ SCI4_TX_BUFFER_SIZE + SCI5_TX_BUFFER_SIZE + SCI6_TX_BUFFER_SIZE + SCI7_TX_BUFFER_SIZE)
#define SCI_BUFFER_SIZE_SUM (SCI_RX_BUFFER_SIZE_SUM + SCI_TX_BUFFER_SIZE_SUM)

#define SCI_RX_BUFFERS_COUNT ((SCI0_RX_BUFFER_SIZE>0) + (SCI1_RX_BUFFER_SIZE>0) + (SCI2_RX_BUFFER_SIZE>0) + \
(SCI3_RX_BUFFER_SIZE>0) + (SCI4_RX_BUFFER_SIZE>0) + (SCI5_RX_BUFFER_SIZE>0) + \
(SCI6_RX_BUFFER_SIZE>0) + (SCI7_RX_BUFFER_SIZE>0))
#define SCI_TX_BUFFERS_COUNT ((SCI0_TX_BUFFER_SIZE>0) + (SCI1_TX_BUFFER_SIZE>0) + (SCI2_TX_BUFFER_SIZE>0) + \
(SCI3_TX_BUFFER_SIZE>0) + (SCI4_TX_BUFFER_SIZE>0) + (SCI5_TX_BUFFER_SIZE>0) + \
(SCI6_TX_BUFFER_SIZE>0)) + (SCI7_TX_BUFFER_SIZE>0)
#define SCI_BUFFERS_COUNT (SCI_RX_BUFFERS_COUNT + SCI_TX_BUFFERS_COUNT)

#define SCI_RING_QUEUE_COUNT SCI_BUFFERS_COUNT
/*
****************************************************************************************
*                                 GLOBAL VARIABLES
****************************************************************************************
*/

#define SCI_RING_BUFS_COUNT SCI0_UCOS_CODE_EN + SCI1_UCOS_CODE_EN + SCI2_UCOS_CODE_EN + SCI3_UCOS_CODE_EN + \
SCI4_UCOS_CODE_EN + SCI5_UCOS_CODE_EN + SCI6_UCOS_CODE_EN + SCI7_UCOS_CODE_EN

SCI_RING_BUF SCIBufs[SCI_RING_BUFS_COUNT];

SCI_RING_BUF * const Ptr_SCIBuf[] = {
#if(SCI0_UCOS_CODE_EN == TRUE)
&SCIBufs[0],
#else
NULL,
#endif
#if(SCI1_UCOS_CODE_EN == TRUE)
&SCIBufs[SCI0_UCOS_CODE_EN],
#else
NULL,
#endif
#if(SCI2_UCOS_CODE_EN == TRUE)
&SCIBufs[SCI0_UCOS_CODE_EN + SCI1_UCOS_CODE_EN],
#else
NULL,
#endif
#if(SCI3_UCOS_CODE_EN == TRUE)
&SCIBufs[SCI0_UCOS_CODE_EN + SCI1_UCOS_CODE_EN + SCI2_UCOS_CODE_EN],
#else
NULL,
#endif
#if(SCI4_UCOS_CODE_EN == TRUE)
&SCIBufs[SCI0_UCOS_CODE_EN + SCI1_UCOS_CODE_EN + SCI2_UCOS_CODE_EN + SCI3_UCOS_CODE_EN],
#else
NULL,
#endif
#if(SCI5_UCOS_CODE_EN == TRUE)
&SCIBufs[SCI0_UCOS_CODE_EN + SCI1_UCOS_CODE_EN + SCI2_UCOS_CODE_EN + SCI3_UCOS_CODE_EN + SCI4_UCOS_CODE_EN],
#else
NULL,
#endif
#if(SCI6_UCOS_CODE_EN == TRUE)
&SCIBufs[SCI0_UCOS_CODE_EN + SCI1_UCOS_CODE_EN + SCI2_UCOS_CODE_EN + SCI3_UCOS_CODE_EN + SCI4_UCOS_CODE_EN \
+ SCI5_UCOS_CODE_EN],
#else
NULL,
#endif
#if(SCI7_UCOS_CODE_EN == TRUE)
&SCIBufs[SCI0_UCOS_CODE_EN + SCI1_UCOS_CODE_EN + SCI2_UCOS_CODE_EN + SCI3_UCOS_CODE_EN + SCI4_UCOS_CODE_EN \
+ SCI5_UCOS_CODE_EN + SCI6_UCOS_CODE_EN],
#else
NULL,
#endif
};
#define pSCIBuf(port) Ptr_SCIBuf[port]

/*
*********************************************************************************************
*                                 LOCAL VARIABLES
*********************************************************************************************
*/

static union{
byte FLAG;
struct {
byte Inited   :1;           //Initialized Flag
byte          :1;
byte          :1;
byte          :1;
byte          :1;
byte          :1;
byte          :1;
byte          :1;
} Bits;
} flag = {0};

// 所有SCI的所有收发缓冲区其实都在同一个大数组中,每个小块分别由不同的环形队列对象管理即可

#if (SCI_BUFFER_SIZE_SUM > 0)
// if you want to put buffers in paged RAM,make sure to configure the RQ_ADDRESSING_MODE in RingQueue.h
// to banked addressing mode or global addressing mode.
// then, uncomment the two #pragma statement will define the buffer in the paged address.
// 如果你想要把缓冲区放在分页RAM区的话,一定要将RingQueue.h中的寻址模式改为分页区寻址或全局寻址,否则编译器
// 无法生成正确的访问代码。
// 然后,取消下面两个pragma注释就会把缓冲区定义到分页区RAM(总大小不能大于4KB),详情请参考相关资料,可以到我的博客找。
//#pragma DATA_SEG __RPAGE_SEG PAGED_RAM
static INT8U SCIBuffer[SCI_BUFFER_SIZE_SUM];
//#pragma DATA_SEG DEFAULT
#else
static INT8U *SCIBuffer = NULL;
#endif

// 管理SCI的所有环形队列对象都在一个数组中
#if (SCI_RING_QUEUE_COUNT > 0)
static RING_QUEUE SCIRingQueues[SCI_BUFFERS_COUNT];
#else
static RING_QUEUE* SCIRingQueues = NULL;
#endif
#define pSCI_RingBufRx(port) (pSCIBuf(port)->RingBufRx)
#define pSCI_RingBufTx(port) (pSCIBuf(port)->RingBufTx)

/*
******************************************************************************************
*                              LOCAL FUNCTION DECLARE
******************************************************************************************
*/

#if(SCI_ARGUMENT_CHECK_EN == TRUE)
#define argCheck1(cond,err,rVal)   if(cond){*perr = (err); return (rVal);}
#define argCheck2(cond,rVal)       if(cond){ return (rVal);}
#define argCheck3(cond)            if(cond){ return ;}
#define argCheck4(cond,err)        if(cond){*perr = (err); return;}
#else
#define argCheck1(cond,err,rVal)
#define argCheck2(cond,rVal)
#define argCheck3(cond)
#define argCheck4(cond,err)
#endif // of (SCI_ARGUMENT_CHECK_EN == TRUE)
#define portCheckTx1(err,rVal)    argCheck1(port >= SCI_CNT || !isSCITxEnable(port),err,rVal)
#define portCheckTx2(rVal)        argCheck2(port >= SCI_CNT || !isSCITxEnable(port),rVal)
#define portCheckTx3()            argCheck3(port >= SCI_CNT || !isSCITxEnable(port))
#define portCheckTx4(err)         argCheck4(port >= SCI_CNT || !isSCITxEnable(port),err)
#define portCheckRx1(err,rVal)    argCheck1(port >= SCI_CNT || !isSCIRxEnable(port),err,rVal)
#define portCheckRx2(rVal)        argCheck2(port >= SCI_CNT || !isSCIRxEnable(port),rVal)
#define portCheckRx3()            argCheck3(port >= SCI_CNT || !isSCIRxEnable(port))
#define portCheckRx4(err)         argCheck4(port >= SCI_CNT || !isSCIRxEnable(port),err)

#pragma CODE_SEG __NEAR_SEG NON_BANKED
// 本地函数不做参数检查,由外部接口函数负责参数的正确
// 内部用来放字符到发送缓冲区, 返回的是SemPend的错误,默认RingQueueIn是不可能出错的(因为有信号量保证有空间可用)
static INT8U _SCI_TxPutCharB(pSCI_RING_BUF pbuf, INT8U c, INT16U timeout);
// 这个函数由Rx ISR来插入一个字符到缓冲区内
static void  _SCI_RxPutCharB (INT8U port, INT8U c);
// 这个函数由Tx ISR调用来从Tx缓冲区内提取下一个字符.如果发现没有字符可以提取了,perr就返回SCI_TX_EMPTY.
//   这是用来通知Tx ISR禁止中断的。
static INT8U _SCI_TxGetCharB (INT8U port, INT8U *perr);
#pragma CODE_SEG DEFAULT

/*
******************************************************************************************
*                                 SCI_BufferInit()
*
* Description :   To initialize the communications module.
*                     初始化通信模型.
*                 You must call this function before calling any other functions.
*                     必须在调用其他函数前调用它.
* Arguments   :
*
* Return:
*
*Note(s):
******************************************************************************************
*/

void  SCI_BufferInit (void)
{
SCI_RING_BUF *pSCIRingBuf;
INT8U err,port;
RING_QUEUE* pRingQueue = SCIRingQueues;
INT8U *__far pBuf = SCIBuffer;    // __far for support SCIBuffer in any address.

// 简单的保护
if(flag.Bits.Inited) return;
// Initialize all buffers
for(port = 0;port < SCI_CNT;port++){
if(!isSCIEnable(port))       // continue when the current port is not enable.
continue;
pSCIRingBuf = pSCIBuf(port);
pSCIRingBuf->port = port;
if(isSCITxEnable(port)) {
pSCIRingBuf->RingBufTxMutex  = OSSemCreate(1);   // use semaphore as mutex
pSCIRingBuf->RingBufTxSem = OSSemCreate(SCITxBufSize(port));
pSCIRingBuf->RingBufTx = RingQueueInit(pRingQueue++,pBuf,SCITxBufSize(port),&err);
pBuf += SCITxBufSize(port);
}
if(isSCIRxEnable(port)) {
pSCIRingBuf->RingBufRxSem = OSSemCreate(0);
pSCIRingBuf->RingBufRx = RingQueueInit(pRingQueue++,pBuf,SCIRxBufSize(port),&err);
pBuf += SCIRxBufSize(port);
}
}
flag.Bits.Inited = 1;
}
/*
******************************************************************************************
*                                      SCI_GetCharB()
*
* Description : To obtain a character from the communications channel.
*                  从通信信道中获取一个字符
*
* Arguments   : port       port can be SCI0 / SCI1;                  选择端口;
*               timeout    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.
*                             调用者最久愿意等待一个字符到达的时钟节拍数,为0表示永久等待.
*               perr        is a pointer to where an error code will be placed: 用于返回故障码:
*                               SCI_NO_ERR      if a character has been received          正确收到字符
*                               SCI_RX_TIMEOUT  if a timeout occurred                     接收超时
*                               SCI_BAD_CH      if you specify an invalid channel number  通道号错误
* Return:       The character in the buffer (or 0 if a timeout occurred); 缓冲区内的字符(或者发生了超时);
* Note(s):
*******************************************************************************************
*/
INT8U  SCI_GetCharB (INT8U port, INT16U timeout, INT8U *perr)
{
#if OS_CRITICAL_METHOD == 3                    /* Allocate storage for CPU status register     */
OS_CPU_SR  cpu_sr = 0u;
#endif
INT8U c;
INT8U oserr;
SCI_RING_BUF *pbuf;
portCheckRx1(SCI_BAD_CH,0);
pbuf = pSCIBuf(port);
OSSemPend(pbuf->RingBufRxSem, timeout, &oserr);  /* Wait for character to arrive             */
if (oserr == OS_TIMEOUT)
{                                                /* See if characters received within timeout*/
*perr = SCI_RX_TIMEOUT;                       /* No, return error code                    */
return (0);
}
else
{
OS_ENTER_CRITICAL();
c = RingQueueOut(pbuf->RingBufRx,&oserr);                   /* Get next one from ring queue.*/
OS_EXIT_CRITICAL();
*perr = SCI_NO_ERR;
return (c);
}
}
/*
**********************************************************************************************
*                                        SCI_PutCharB_Mutex()
*
* Description : 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.
*
*               equal to call:
*                  SCI_SendBuf_hold (port, timeout, &perr);
*                  perr =  SCI_PutCharB(port, INT8U c, INT16U timeout);
*                  SCI_SendBuf_release(port);
*
* Arguments   : port      port can be SCI0 / SCI1;                      选择端口;
*               c         the character to send;                        要发送的字符;
*               timeout   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.
*                         在缓冲区满的情况下, 调用者最久愿意等待的时钟节拍数,为0表示永久等待.
* Return:      SCI_NO_ERR       if the character was placed in the Tx buffer;
*                                    字符成功放入Tx缓冲区内;
*              SCI_TX_TIMEOUT   if the buffer didn't empty or can't access within the specified timeout period;
*                                    超时后还是没有清空缓冲区或者还无法访问
*              SCI_BAD_CH       if you specify an invalid channel number  通道号错误
*              SCI_PEND_ISR     If you called this function from an ISR and the result would lead to a suspension.
*                                    在ISR中调用了这个函数并且需要等待
*              SCI_PEND_LOCKED  If you called this function when the scheduler is locked.
*                                    如果当系统调度器被锁住的时候调用的这个函数
*              SCI_ERR_UNKNOWN
* Note(s): 1. Don't use this function in ISR routine
*             不要在ISR中使用这个函数
**********************************************************************************************
*/
INT8U SCI_PutCharB_Mutex (INT8U port, INT8U c, INT16U timeout)
{
pSCI_RING_BUF pbuf;
INT8U oserr;
portCheckTx2(SCI_BAD_CH);
pbuf = pSCIBuf(port);
OSSemPend(pbuf->RingBufTxMutex,timeout,&oserr);  /* Wait for the right of using Tx buffer*/
if(oserr == OS_ERR_NONE){                        /* If get the right then */
oserr =  _SCI_TxPutCharB(pbuf,c,timeout);      /*  Put INT8U into Tx buffer*/
OSSemPost(pbuf->RingBufTxMutex);               /* Release the the right of use Tx buffer*/
}else if (oserr == OS_TIMEOUT)                          /* If Timeout happen */
return (SCI_TX_TIMEOUT);                      /* Timed out, return error code             */
else if (oserr == OS_ERR_PEND_ISR)
return (SCI_PEND_ISR);
else if (oserr == OS_ERR_PEND_LOCKED)
return (SCI_PEND_LOCKED);
else
return (SCI_ERR_UNKNOWN);
return (SCI_NO_ERR);
}
/*
*********************************************************************************************
*                                        SCI_PutCharsB_Mutex()
*
* Description : This function is called by your application to send chars 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.
*                        equal to call:
*                            SCI_SendBuf_hold (port, timeout, &perr);
*                            perr =  SCI_PutCharsB(port, INT8U c, INT16U timeout);
*                            SCI_SendBuf_release(port);
*
* Arguments   : port       port can be SCI0 / SCI1;          选择端口;
*               buf        the buffer;                       要发送的字符;
*               length     the length of the buffer;
*               timeout    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.
*                                  在缓冲区满的情况下, 调用者最久愿意等待的时钟节拍数,为0表示永久等待.
* Return:      SCI_NO_ERR       if the character was placed in the Tx buffer;
*                                    字符成功放入Tx缓冲区内;
*              SCI_TX_TIMEOUT   if the buffer didn't empty or can't access within the specified timeout period;
*                                    超时后还是没有清空缓冲区或者还无法访问
*              SCI_BAD_CH       if you specify an invalid channel number  通道号错误
*              SCI_PEND_ISR     If you called this function from an ISR and the result would lead to a suspension.
*                                    在ISR中调用了这个函数并且需要等待
*              SCI_PEND_LOCKED  If you called this function when the scheduler is locked.
*                                    如果当系统调度器被锁住的时候调用的这个函数
*              SCI_ERR_UNKNOWN
* Note(s): 1. Don't use this function in ISR routine
*             不要在ISR中使用这个函数
***********************************************************************************************
*/
INT8U SCI_PutCharsB_Mutex (INT8U port, const INT8U *buf,INT16U length, INT16U timeout) {
pSCI_RING_BUF pbuf;
INT8U oserr;
portCheckTx2(SCI_BAD_CH);
pbuf = pSCIBuf(port);
OSSemPend(pbuf->RingBufTxMutex,timeout,&oserr);        /* Wait for the right of use Tx buffer*/
if(oserr == OS_ERR_NONE){
while(length--){
oserr =  _SCI_TxPutCharB(pbuf,*buf++,timeout);     /*  Put INT8U into Tx buffer*/
if (oserr != OS_ERR_NONE)                           /* if any err happen */
break;                                         /* break */
}
OSSemPost(pbuf->RingBufTxMutex);
}else if (oserr == OS_TIMEOUT)                          /* If Timeout happen */
return (SCI_TX_TIMEOUT);                      /* Timed out, return error code             */
else if (oserr == OS_ERR_PEND_ISR)
return (SCI_PEND_ISR);
else if (oserr == OS_ERR_PEND_LOCKED)
return (SCI_PEND_LOCKED);
else
return (SCI_ERR_UNKNOWN);
return (SCI_NO_ERR);
}
/*
********************************************************************************************
*                                 SCI_PutStringB_Mutex()
*
* Description : This function is called by your application to send a string,which ends with '\0', 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
*                 ill be enabled.
*
* Arguments   : port       port can be SCI0 / SCI1;                  选择端口;
*               buf        the buffer;                       要发送的字符;
*               length     the length of the buffer;
*               timeout    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.
*                            在缓冲区满的情况下, 调用者最久愿意等待的时钟节拍数,为0表示永久等待.
* Return:      SCI_NO_ERR        if the character was placed in the Tx buffer;  字符成功放入Tx缓冲区内;
*              SCI_TX_TIMEOUT    if the buffer didn't empty or can't access within the specified timeout period;
*                                    超时后还是没有清空缓冲区或者还无法访问
*              SCI_BAD_CH        if you specify an invalid channel number  通道号错误
*Note(s):
**********************************************************************************************
*/
//INT8U SCI_PutStringB_Mutex(INT8U port, const INT8U *str, INT16U timeout){
//   return SCI_PutCharsB_Mutex(port,str,strlen(str),timeout);
//}

/*
*********************************************************************************************
*                                        SCI_SendBuf_hold()
*
* Description : This function is called by your application to own the exclusive right of ...
*                ...sending INT8U on particular SCI port. your function should look like this:
*
*                    SCI_SendBuf_hold(port,timeout,&err);
*                    if (*err == SCI_NO_ERR){
*                       .......
*                        SCI_PutCharB(port,c,&err);
*                       .......
*                    }
*                    SCI_SendBuf_release(port);
*
* Arguments : port      port can be SCI0/SCI1;                  选择端口;
*             timeout   is the timeout (in clock ticks) to wait in case the buffer is occupied.  If you ...
*                       ...specify a timeout of 0, the function will wait forever for the buffer to empty.
*                             在其他地方占有使用权的情况下, 调用者最久愿意等待的时钟节拍数,为0表示永久等待.
*             perr      SCI_NO_ERR       if get the exclusive right of sending INT8U on particular SCI port successfully;
*                                            成功获取对应端口的发送权
*                       SCI_TX_TIMEOUT   if can't get the exclusive right of sending INT8U on particular...
*                                          ... SCI port within the specified timeout period;
*                                            超时后还是没有获得对应端口的发送权
*                       SCI_BAD_CH       if you specify an invalid channel number.  通道号错误
*                       SCI_PEND_ISR     If you called this function from an ISR and the result would lead to a suspension.
*                                            在ISR中调用了这个函数并且需要等待
*                       SCI_PEND_LOCKED  If you called this function when the scheduler is locked.
*                                            如果当系统调度器被锁住的时候调用的这个函数
*                       SCI_ERR_UNKNOWN
* Note(s): 1. Don't use this function in ISR routine
*             不要在ISR中使用这个函数
*          2.Once call this function successfully, you must call SCI_SendBuf_release in the end of your function.  ...
*               ... Otherwise, other caller to SCI_SendBuf_hold or SCI_XXX_Mutex mothod will fail.
********************************************************************************************
*/
void  SCI_SendBuf_hold(INT8U port, INT16U timeout, INT8U *perr){
portCheckTx4(SCI_BAD_CH);
OSSemPend(pSCIBuf(port)->RingBufTxMutex,timeout,perr);        /* Wait for the right of use Tx buffer*/
if(*perr == OS_ERR_NONE)
*perr = SCI_NO_ERR;
else if(*perr == OS_TIMEOUT)
*perr = SCI_TX_TIMEOUT;                      /* Timed out, return error code             */
else if(*perr == OS_ERR_PEND_ISR)
*perr = SCI_PEND_ISR;
else if(*perr == OS_ERR_PEND_LOCKED)
*perr = SCI_PEND_LOCKED;
else
*perr = SCI_ERR_UNKNOWN;
}
/*
*********************************************************************************************
*                                        SCI_PutCharB()
*
* Description : This function is called by your application to send INT8U 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.
*
* Arguments   : port     port can be SCI0 / SCI1;                  选择端口;
*               c        the INT8U to send;                       要发送的字符;
*               timeout  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.
*                           在缓冲区满的情况下, 调用者最久愿意等待的时钟节拍数,为0表示永久等待.
*
* Return:       SCI_NO_ERR        if the character was placed in the Tx buffer;
*                                     字符成功放入Tx缓冲区内;
*               SCI_TX_TIMEOUT    if the buffer didn't empty within the specified timeout period;
*                                     超时后还是没有清空缓冲区
*               SCI_BAD_CH        if you specify an invalid channel number  通道号错误
*               SCI_PEND_ISR      If you called this function from an ISR and the result would lead to a suspension.
*                                    在ISR中调用了这个函数并且需要等待
*               SCI_PEND_LOCKED   If you called this function when the scheduler is locked.
*                                    如果当系统调度器被锁住的时候调用的这个函数
*               SCI_ERR_UNKNOWN
* Note(s): 1.  Don't use this function in ISR routine
*               不要在ISR中使用这个函数
*          2.  you should call SCI_SendBuf_hold() first unless you know what you are doing.
*               调用这个函数前你应该先调用SCI_SendBuf_hold(),除非你知道你在干什么
*********************************************************************************************
*/
INT8U SCI_PutCharB (INT8U port, INT8U c, INT16U timeout){
SCI_RING_BUF *pbuf;
INT8U oserr;
portCheckTx2(SCI_BAD_CH);
pbuf = pSCIBuf(port);
oserr =  _SCI_TxPutCharB(pbuf,c,timeout);            /*  Put INT8U into Tx buffer*/
if (oserr == OS_ERR_NONE)
return (SCI_NO_ERR);
else if (oserr == OS_TIMEOUT)                        /* If Timeout happen */
return (SCI_TX_TIMEOUT);                           /* Timed out, return error code   */
else if (oserr == OS_ERR_PEND_ISR)
return (SCI_PEND_ISR);
else if (oserr == OS_ERR_PEND_LOCKED)
return (SCI_PEND_LOCKED);
else
return (SCI_ERR_UNKNOWN);
}
/*
*********************************************************************************************
*                                        SCI_PutCharsB()
*
* Description : This function is called by your application to send INT8U 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.
*
* Arguments   : port     port can be SCI0 / SCI1;         选择端口;
*               buf      the buffer;                      要发送的缓冲区;
*               length   the length of the buffer;        缓冲区长度;
*               timeout  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.
*                         在缓冲区满的情况下, 调用者最久愿意等待的时钟节拍数,为0表示永久等待.
*
* Return:       SCI_NO_ERR      if the character was placed in the Tx buffer;
*                               字符成功放入Tx缓冲区内;
*               SCI_TX_TIMEOUT  if the buffer didn't empty within the specified timeout period;
*                               超时后还是没有清空缓冲区
*               SCI_BAD_CH      if you specify an invalid channel number  通道号错误
*               SCI_PEND_ISR    If you called this function from an ISR and the result would lead to a suspension.
*                                    在ISR中调用了这个函数并且需要等待
*               SCI_PEND_LOCKED If you called this function when the scheduler is locked.
*                                    如果当系统调度器被锁住的时候调用的这个函数
*               SCI_ERR_UNKNOWN
* Note(s): 1.  Don't use this function in ISR routine
*               不要在ISR中使用这个函数
*          2.  you should call SCI_SendBuf_hold() first unless you know what you are doing.
*               调用这个函数前你应该先调用SCI_SendBuf_hold(),除非你知道你在干什么
**********************************************************************************************
*/
INT8U SCI_PutCharsB(INT8U port, const INT8U *buf,INT16U length, INT16U timeout) {
SCI_RING_BUF *pbuf;
INT8U oserr;
portCheckTx2(SCI_BAD_CH);
pbuf = pSCIBuf(port);
while(length--){
oserr =  _SCI_TxPutCharB(pbuf,*buf++,timeout);     /*  Put INT8U into Tx buffer*/
if(oserr != OS_ERR_NONE)                           /* if any err happen */
break;                                           /* break */
}
if (oserr == OS_ERR_NONE)
return (SCI_NO_ERR);
else if (oserr == OS_TIMEOUT)                        /* If Timeout happen */
return (SCI_TX_TIMEOUT);                           /* Timed out, return error code   */
else if (oserr == OS_ERR_PEND_ISR)
return (SCI_PEND_ISR);
else if (oserr == OS_ERR_PEND_LOCKED)
return (SCI_PEND_LOCKED);
else
return (SCI_ERR_UNKNOWN);
}

/*
*********************************************************************************************
*                                   SCI_SendBuf_release()
*
* Description :  This function is called by your application to release the exclusive right...
*                 ... of sending INT8U on particular SCI port.
*
* Arguments   : port   port can be SCI0/SCI1;                  选择端口;
*
* Return:
*
*Note(s):
*********************************************************************************************
*/
void  SCI_SendBuf_release(INT8U port){
portCheckTx3();
OSSemPost(pSCIBuf(port)->RingBufTxMutex);
}

/*
**********************************************************************************************
*                                        SCI_RxBufferIsEmpty()
*
* Description : To see if any character is available from the communications channel.
*                 查看是否接收缓冲区为空
*
* Arguments   : 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).
*               0xff     if you specify an invalid channel number.
*               如果缓冲区内存在字符,则返回FALSE,否则返回TRUE. 0xff 如果通道号无效.
*Note(s):
**********************************************************************************************
*/
INT8U  SCI_RxBufferIsEmpty (INT8U port)
{
#if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register     */
OS_CPU_SR  cpu_sr = 0u;
#endif
INT8U empty;
portCheckRx2(0xff);
OS_ENTER_CRITICAL();
empty =  RingQueueIsEmpty(pSCIBuf(port)->RingBufRx);
OS_EXIT_CRITICAL();
return (empty);
}
/*
********************************************************************************************
*                                        SCI_TxBufferIsFull()
*
* Description: To see if any more characters can be placed in the Tx buffer.
*                  是否发送缓冲区已满
*
* Arguments  :  port    port can be SCI0 / SCI1;                  选择端口;
*
* Return     : If the buffer is full, the function returns TRUE. Otherwise, the function returns FALSE.
*              0xff                if you specify an invalid channel number.
*              如果发送缓冲区满,则返回TRUE,否则返回FALSE. 0xff 如果通道号无效.
*Note(s):
*********************************************************************************************
*/
INT8U SCI_TxBufferIsFull (INT8U port)
{
#if OS_CRITICAL_METHOD == 3u                    /* Allocate storage for CPU status register     */
OS_CPU_SR  cpu_sr = 0u;
#endif
INT8U isfull;
SCI_RING_BUF *pbuf;
portCheckTx2(0xff);
pbuf = pSCIBuf(port);
OS_ENTER_CRITICAL();
isfull = RingQueueIsFull(pbuf->RingBufTx);
OS_EXIT_CRITICAL();
return (isfull);
}

/*
*********************************************************************************************************
*                                LOCAL FUNCTION
*********************************************************************************************************
*/
#pragma CODE_SEG __NEAR_SEG NON_BANKED
// 内部用来放字符到发送缓冲区, 返回的是SemPend的错误,默认RingQueueIn是不可能出错的(因为有信号量保证有空间可用)
static INT8U _SCI_TxPutCharB(pSCI_RING_BUF pbuf, INT8U c, INT16U timeout){
#if OS_CRITICAL_METHOD == 3u                             /* Allocate storage for CPU status register */
OS_CPU_SR  cpu_sr = 0u;
#endif
INT8U err,tmperr;
OSSemPend(pbuf->RingBufTxSem, timeout, &err);        /* Wait for space in Tx buffer              */
if(err == OS_ERR_NONE){
OS_ENTER_CRITICAL();
/* Get next one from ring queue, and see whether this is the first character       */
if (RingQueueIn(pbuf->RingBufTx,c,RQ_OPTION_WHEN_FULL_DISCARD_FIRST,&tmperr) == 1)
SCI_EnableTxInt(pbuf->port);                       /* Yes, Enable Tx interrupts                */
OS_EXIT_CRITICAL();
}
return err;
}

// This function is called by the Rx ISR to insert a character into the receive ring buffer.
// 这个函数由Rx ISR来插入一个字符到缓冲区内
static void  _SCI_RxPutCharB (INT8U port, INT8U c)
{
pSCI_RING_BUF pbuf;
INT8U oserr;
pbuf =  pSCIBuf(port);
RingQueueIn(pbuf->RingBufRx,c,RQ_OPTION_WHEN_FULL_DISCARD_FIRST,&oserr);
if(oserr == RQ_ERR_NONE)
(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 perr will return SCI_TX_EMPTY if the buffer is empty.  This is done to signal the Tx ISR
//   to disable interrupts because there is no char to send.
// 这个函数由Tx ISR调用来从Tx缓冲区内提取下一个字符.如果发现没有字符可以提取了,perr就返回SCI_TX_EMPTY.
//    这是用来通知Tx ISR禁止中断的。
static INT8U _SCI_TxGetCharB (INT8U port, INT8U *perr)
{
INT8U c;
pSCI_RING_BUF pbuf;
pbuf =  pSCIBuf(port);
c = RingQueueOut(pbuf->RingBufTx,perr);                   /* Get next one from ring queue.*/
if(*perr == RQ_ERR_NONE){
OSSemPost(pbuf->RingBufTxSem);
*perr = SCI_NO_ERR;
return c;
}else{
*perr = SCI_TX_EMPTY;
return (FALSE);                                      /* Buffer is empty   */
}
}

static void SCI_ISR_Handler(INT8U port){
INT8U status,err;
volatile INT8U data;
status = SCISR1(port);
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 = SCIDRL(port);           // in case that compiler optimize this assignment,the 'data' must prefix volatile
}
else if(status & 0x20) //Receive Data Reg Full Flag
{
_SCI_RxPutCharB(port, SCIDRL(port));                    // Insert received character into buffer
}
else if(status & 0x80)
{
data = _SCI_TxGetCharB(port, &err);             // Get next character to send.
if (err == SCI_TX_EMPTY)
{                                            // Do we have anymore characters to send ?
// No,  Disable Tx interrupts
SCI_DisableTxInt(port);
}
else
{
SCIDRL(port) = data;        // Yes, Send character
}
}
}
#pragma CODE_SEG DEFAULT
void SCI0_ISR_Handler(void)
{
#if(SCI0_UCOS_CODE_EN == TRUE)
SCI_ISR_Handler(SCI0);
#endif
}

void SCI1_ISR_Handler (void)
{
#if(SCI1_UCOS_CODE_EN == TRUE)
SCI_ISR_Handler(SCI1);
#endif
}

void SCI2_ISR_Handler(void)
{
#if(SCI2_UCOS_CODE_EN == TRUE)
SCI_ISR_Handler(SCI2);
#endif
}

void SCI3_ISR_Handler (void)
{
#if(SCI3_UCOS_CODE_EN == TRUE)
SCI_ISR_Handler(SCI3);
#endif
}

void SCI4_ISR_Handler(void)
{
#if(SCI4_UCOS_CODE_EN == TRUE)
SCI_ISR_Handler(SCI4);
#endif
}

void SCI5_ISR_Handler (void)
{
#if(SCI5_UCOS_CODE_EN == TRUE)
SCI_ISR_Handler(SCI5);
#endif
}

void SCI6_ISR_Handler(void)
{
#if(SCI6_UCOS_CODE_EN == TRUE)
SCI_ISR_Handler(SCI6);
#endif
}

void SCI7_ISR_Handler(void)
{
#if(SCI7_UCOS_CODE_EN == TRUE)
SCI_ISR_Handler(SCI7);
#endif
}

#endif  // of (SCI_MODULE_ENABLE == TRUE && SCI_UCOS_MODULE_ENABLE == TRUE)


头文件就不再发了,在上一章中。

其中使用到了环形缓冲区,具体实现可以到我的另一篇博文中找到。还使用了uCOS-II嵌入式操作系统提供的互斥型信号量以及信号量来保证不同任务对SCI口缓冲区的访问不发生冲突。每个SCI口都会对应一个接收以及一个发送缓冲区,对应的大小可以在.h文件中配置。

这个驱动的主要作用就是为每个SCI口提供缓冲区,一方面,SCI模块会不断的从发送缓冲区内获取字符并发送,另一方面,它会把接收到的字符不断放入接收缓冲区,这样所有经过SCI口的字节流都经过缓冲,有效缓解了处理不及时等现象。提供的函数也是围绕缓冲区的。

首先使用这个模块前一定要先调用SCI_BufferInit函数来进行初始化。

而由于一般来说一个SCI口只会有一个任务来接收SCI口的字节流,没有竞争的问题,所以只提供了SCI_GetCharB函数来监听SCI口(和硬件驱动提供的SCI_GetChar相比多了一个B,即Buffer)。一般来说会专门开个任务阻塞地监听这个信道,类似这样:

static  void  SCI0ListenTask (void *p_arg){
INT8U c,err;
……
while(true) {
c = SCI_GetCharB(SCI0,RX_MAX_TIME_INTERVAL,&err); //从SCI0获得一个字符,最大等待RX_MAX_TIME_INTERVAL次TICK。
if( err == SCI_RX_TIMEOUT ){          //超时导致的返回
……          // 超时的处理
continue;
}
// 正常获得字符后的处理
……
}
}


而对于发送,因为有不同任务同时想用一个SCI口发送的问题,所以提供了互斥的方法,即SCI_PutCharB_Mutex和SCI_PutCharsB_Mutex,只要保证所有地方都使用这些互斥的方法发送,就可以保证不同任务间的发送互不干扰。

比如:

任务一发送:abcde\r
任务二发送:12345\r


如果不使用互斥/独占的方法发送的话(比如直接调用硬件驱动提供的函数),由于任务调度的原因,可能最后另外一头收到的就成了:abc12de\r345\r了。

而如果使用互斥的方法的话,另一头就能保证收到的是 abcde\r12345\r

另外,还提供的等价的互斥发送方法:SCI_SendBuf_hold、SCI_PutCharB、SCI_PutCharsB和SCI_SendBuf_release,他们需要成套使用,先用hold获取发送权,然后发送字节,最后释放发送权。这样就能保证在hold和release之间发送的字节的连续性了。

就讲到这里把,有什么建议或意见请留言。

更改历史:

2017/10/29 更新到 V2.1 支持了所有的SCI口。

2018/02/24 更新到 V2.2 例行优化
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息