[嵌入式开发模块]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操作系统的要求写的中断服务程序。
要记得修改中断向量表把用到的端口的ISR分别指向其中断地址,如SCI0_RxTxISR指向0xFFD6、SCI1_RxTxISR1指向0xFFD4,也就是SCI0和SCI1的中断地址。
然后是.c文件:
头文件就不再发了,在上一章中。
其中使用到了环形缓冲区,具体实现可以到我的另一篇博文中找到。还使用了uCOS-II嵌入式操作系统提供的互斥型信号量以及信号量来保证不同任务对SCI口缓冲区的访问不发生冲突。每个SCI口都会对应一个接收以及一个发送缓冲区,对应的大小可以在.h文件中配置。
这个驱动的主要作用就是为每个SCI口提供缓冲区,一方面,SCI模块会不断的从发送缓冲区内获取字符并发送,另一方面,它会把接收到的字符不断放入接收缓冲区,这样所有经过SCI口的字节流都经过缓冲,有效缓解了处理不及时等现象。提供的函数也是围绕缓冲区的。
首先使用这个模块前一定要先调用SCI_BufferInit函数来进行初始化。
而由于一般来说一个SCI口只会有一个任务来接收SCI口的字节流,没有竞争的问题,所以只提供了SCI_GetCharB函数来监听SCI口(和硬件驱动提供的SCI_GetChar相比多了一个B,即Buffer)。一般来说会专门开个任务阻塞地监听这个信道,类似这样:
而对于发送,因为有不同任务同时想用一个SCI口发送的问题,所以提供了互斥的方法,即SCI_PutCharB_Mutex和SCI_PutCharsB_Mutex,只要保证所有地方都使用这些互斥的方法发送,就可以保证不同任务间的发送互不干扰。
比如:
如果不使用互斥/独占的方法发送的话(比如直接调用硬件驱动提供的函数),由于任务调度的原因,可能最后另外一头收到的就成了: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 例行优化
首先先发一个自己封装的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 例行优化
相关文章推荐
- [嵌入式开发模块]MC9S12XEP100 SCI(UART)驱动程序
- [嵌入式开发模块]MC9S12XEP100 SPI模块 驱动程序
- [嵌入式开发模块]MC9S12XEP100 ATD模块 驱动程序
- Xilinx FPGA嵌入式开发(五)- XPS中的Uart(Lite)模块
- ARM硬件平台上基于UCOS移植Lwip网络协议栈 分类: 嵌入式开发学习 2015-06-14 10:33 55人阅读 评论(1) 收藏
- 基于ARM体系结构的嵌入式开发(1)-----移植第一个驱动程序
- 基于嵌入式操作系统的USB驱动程序开发
- 基于ARM的简单的嵌入式web服务器(ucos-ii)中文
- uCOS-II的嵌入式串口通信模块设计
- 基于嵌入式操作系统的USB驱动程序开发
- 基于NFS的嵌入式linux设备驱动程序开发环境搭建
- (转载)uCOS-II的嵌入式串口通信模块设计
- 【EVB-335X-II试用体验】 基于Yocto的嵌入式的敏捷项目开发:以电子相册为例
- 嵌入式开发:基于计算机模块或芯片开发的比较
- 基于ARM体系结构的嵌入式开发(1)-----移植第一个驱动程序
- 基于嵌入式Linux系统设备驱动程序的开发
- [嵌入式开发模块]4X4矩阵键盘扫描 基于MC9S12XEP100
- 基于ARM的简单的嵌入式web服务器(ucos-ii)
- 基于WinDriver的驱动程序的开发
- 基于HK-2000 OEM的嵌入式Linux开发