在上一篇学习笔记《STM32F10x 学习笔记6(USART实现串口通讯 2)》给出了个利用环形缓冲区的串口驱动。最近研究了uCOS-II在STM32上的移植。下面再给个利用uCOS-II的信号量的串口驱动。整个驱动的基本框架和上一篇没什么区别,所以不多介绍。直接贴代码:
整个驱动包含四个文件:
uart.h
uart.c
COMMRTOS.H
COMMRTOS.c
其中前两个文件是对串口基本功能的封装。
uart.h 的代码如下:
#ifndef _UART_H_ #define _UART_H_ void USART1_Init(void); void USART2_Init(void); void UART_PutChar(USART_TypeDef* USARTx, uint8_t Data); void UART_PutStr (USART_TypeDef* USARTx, uint8_t *str); uint8_t UART_GetChar(USART_TypeDef* USARTx); #endif
#include "stm32f10x.h" void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); /* Configure USART Tx as alternate function push-pull */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Configure USART Rx as input floating */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void USART2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE); /* Configure USART Tx as alternate function push-pull */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD, &GPIO_InitStructure); /* Configure USART Rx as input floating */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, &USART_InitStructure); USART_Cmd(USART2, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void UART_PutChar(USART_TypeDef* USARTx, uint8_t Data) { //while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET ) {}; while((USARTx->SR & USART_FLAG_TXE) == 0x00) {}; //USART_SendData (USARTx, Data); USARTx->DR = Data; } void UART_PutStr(USART_TypeDef* USARTx, uint8_t *str) { while(0 != *str) { UART_PutChar(USARTx, *str); str++; } } uint8_t UART_GetChar(USART_TypeDef* USARTx) { //while( USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET) {}; while((USARTx->SR & USART_FLAG_RXNE) == 0x00) {}; //return USART_ReceiveData(USARTx); return (USARTx->DR & 0xff); }
#ifndef _COMMRTOS_H_ #define _COMMRTOS_H_ #ifndef CFG_H #define COMM_RX_BUF_SIZE 64 /* Number of characters in Rx ring buffer */ #define COMM_TX_BUF_SIZE 64 /* Number of characters in Tx ring buffer */ #endif /* ********************************************************************************************************* * CONSTANTS ********************************************************************************************************* */ #ifndef FALSE #define FALSE 0x00 #endif #ifndef TRUE #define TRUE 0xff #endif #ifndef NUL #define NUL 0x00 #endif #define COM1 0 #define COM2 1 /* ERROR CODES */ #define COMM_NO_ERR 0 /* Function call was successful */ #define COMM_BAD_CH 1 /* Invalid communications port channel */ #define COMM_RX_EMPTY 2 /* Rx buffer is empty, no character available */ #define COMM_TX_FULL 3 /* Tx buffer is full, could not deposit character */ #define COMM_TX_EMPTY 4 /* If the Tx buffer is empty. */ #define COMM_RX_TIMEOUT 5 /* If a timeout occurred while waiting for a character*/ #define COMM_TX_TIMEOUT 6 /* If a timeout occurred while waiting to send a char.*/ #define COMM_PARITY_NONE 0 /* Defines for setting parity */ #define COMM_PARITY_ODD 1 #define COMM_PARITY_EVEN 2 unsigned char CommGetChar(unsigned char ch, unsigned short to, unsigned char *err); void COMInit(void); unsigned char CommIsEmpty(unsigned char ch); unsigned char CommIsFull(unsigned char ch); unsigned char CommPutChar(unsigned char ch, unsigned char c, unsigned short to); void CommPutStr(unsigned char ch, uint8_t *str); #endif
commrtos.c 的代码如下:
#include "stm32f10x_usart.h" #include "includes.h" #include "COMMRTOS.H" /* * DATA TYPES */ typedef struct { unsigned short RingBufRxCtr; /* Number of characters in the Rx ring buffer */ OS_EVENT *RingBufRxSem; /* Pointer to Rx semaphore */ unsigned char *RingBufRxInPtr; /* Pointer to where next character will be inserted */ unsigned char *RingBufRxOutPtr; /* Pointer from where next character will be extracted */ unsigned char RingBufRx[COMM_RX_BUF_SIZE]; /* Ring buffer character storage (Rx) */ unsigned short RingBufTxCtr; /* Number of characters in the Tx ring buffer */ OS_EVENT *RingBufTxSem; /* Pointer to Tx semaphore */ unsigned char *RingBufTxInPtr; /* Pointer to where next character will be inserted */ unsigned char *RingBufTxOutPtr; /* Pointer from where next character will be extracted */ unsigned char RingBufTx[COMM_TX_BUF_SIZE]; /* Ring buffer character storage (Tx) */ } COMM_RING_BUF; /* * GLOBAL VARIABLES */ COMM_RING_BUF Comm1Buf; COMM_RING_BUF Comm2Buf; static void COMEnableTxInt(unsigned char port) { static USART_TypeDef* map[2] = {USART1, USART2}; //USART_ITConfig(map[port], USART_IT_TXE, ENABLE); map[port]->CR1 |= USART_FLAG_TXE; } /* ********************************************************************************************************* * REMOVE CHARACTER FROM RING BUFFER * * * Description : This function is called by your application to obtain a character from the communications * channel. The function will wait for a character to be received on the serial channel or * until the function times out. * Arguments : 'ch' is the COMM port channel number and can either be: * COMM1 * COMM2 * 'to' is the amount of time (in clock ticks) that the calling function is willing to * wait for a character to arrive. If you specify a timeout of 0, the function will * wait forever for a character to arrive. * 'err' is a pointer to where an error code will be placed: * *err is set to COMM_NO_ERR if a character has been received * *err is set to COMM_RX_TIMEOUT if a timeout occurred * *err is set to COMM_BAD_CH if you specify an invalid channel number * Returns : The character in the buffer (or NUL if a timeout occurred) ********************************************************************************************************* */ unsigned char CommGetChar(unsigned char ch, unsigned short to, unsigned char *err) { unsigned char c; unsigned char oserr; COMM_RING_BUF *pbuf; OS_CPU_SR cpu_sr; switch(ch) { /* Obtain pointer to communications channel */ case COM1: pbuf = &Comm1Buf; break; case COM2: pbuf = &Comm2Buf; break; default: *err = COMM_BAD_CH; return (NUL); } OSSemPend(pbuf->RingBufRxSem, to, &oserr); /* Wait for character to arrive */ if(oserr == OS_TIMEOUT) { /* See if characters received within timeout*/ *err = COMM_RX_TIMEOUT; /* No, return error code */ return (NUL); } else { OS_ENTER_CRITICAL(); pbuf->RingBufRxCtr--; /* Yes, decrement character count */ c = *pbuf->RingBufRxOutPtr++; /* Get character from buffer */ if(pbuf->RingBufRxOutPtr == &pbuf->RingBufRx[COMM_RX_BUF_SIZE]) { /* Wrap OUT pointer */ pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0]; } OS_EXIT_CRITICAL(); *err = COMM_NO_ERR; return (c); } } void CommPutStr(unsigned char ch, uint8_t *str) { while(0 != *str) { CommPutChar(ch, *str, 0); str++; } } static unsigned char COMGetTxChar(unsigned char ch, unsigned char *err) { unsigned char c; COMM_RING_BUF *pbuf; switch(ch) { /* Obtain pointer to communications channel */ case COM1: pbuf = &Comm1Buf; break; case COM2: pbuf = &Comm2Buf; break; default: *err = COMM_BAD_CH; return (NUL); } if(pbuf->RingBufTxCtr > 0) { /* See if buffer is empty */ pbuf->RingBufTxCtr--; /* No, decrement character count */ c = *pbuf->RingBufTxOutPtr++; /* Get character from buffer */ if(pbuf->RingBufTxOutPtr == &pbuf->RingBufTx[COMM_TX_BUF_SIZE]) { /* Wrap OUT pointer */ pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0]; } OSSemPost(pbuf->RingBufTxSem); /* Indicate that character will be sent */ *err = COMM_NO_ERR; return (c); /* Characters are still available */ } else { *err = COMM_TX_EMPTY; return (NUL); /* Buffer is empty */ } } /* ********************************************************************************************************* * INITIALIZE COMMUNICATIONS MODULE * Description : This function is called by your application to initialize the communications module. You * must call this function before calling any other functions. * Arguments : none ********************************************************************************************************* */ void COMInit(void) { COMM_RING_BUF *pbuf; pbuf = &Comm1Buf; /* Initialize the ring buffer for COMM1 */ pbuf->RingBufRxCtr = 0; pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0]; pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0]; pbuf->RingBufRxSem = OSSemCreate(0); pbuf->RingBufTxCtr = 0; pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0]; pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0]; pbuf->RingBufTxSem = OSSemCreate(COMM_TX_BUF_SIZE); pbuf = &Comm2Buf; /* Initialize the ring buffer for COMM2 */ pbuf->RingBufRxCtr = 0; pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0]; pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0]; pbuf->RingBufRxSem = OSSemCreate(0); pbuf->RingBufTxCtr = 0; pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0]; pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0]; pbuf->RingBufTxSem = OSSemCreate(COMM_TX_BUF_SIZE); } /* ********************************************************************************************************* * SEE IF RX CHARACTER BUFFER IS EMPTY * * * Description : This function is called by your application to see if any character is available from the * communications channel. If at least one character is available, the function returns * FALSE otherwise, the function returns TRUE. * Arguments : 'ch' is the COMM port channel number and can either be: * COMM1 * COMM2 * Returns : TRUE if the buffer IS empty. * FALSE if the buffer IS NOT empty or you have specified an incorrect channel. ********************************************************************************************************* */ unsigned char CommIsEmpty(unsigned char ch) { unsigned char empty; COMM_RING_BUF *pbuf; OS_CPU_SR cpu_sr; switch(ch) { /* Obtain pointer to communications channel */ case COM1: pbuf = &Comm1Buf; break; case COM2: pbuf = &Comm2Buf; break; default: return (TRUE); } OS_ENTER_CRITICAL(); if(pbuf->RingBufRxCtr > 0) { /* See if buffer is empty */ empty = FALSE; /* Buffer is NOT empty */ } else { empty = TRUE; /* Buffer is empty */ } OS_EXIT_CRITICAL(); return (empty); } /* ********************************************************************************************************* * SEE IF TX CHARACTER BUFFER IS FULL * Description : This function is called by your application to see if any more characters can be placed * in the Tx buffer. In other words, this function check to see if the Tx buffer is full. * If the buffer is full, the function returns TRUE otherwise, the function returns FALSE. * Arguments : 'ch' is the COMM port channel number and can either be: * COMM1 * COMM2 * Returns : TRUE if the buffer IS full. * FALSE if the buffer IS NOT full or you have specified an incorrect channel. ********************************************************************************************************* */ unsigned char CommIsFull(unsigned char ch) { unsigned char full; COMM_RING_BUF *pbuf; OS_CPU_SR cpu_sr; switch(ch) { /* Obtain pointer to communications channel */ case COM1: pbuf = &Comm1Buf; break; case COM2: pbuf = &Comm2Buf; break; default: return (TRUE); } OS_ENTER_CRITICAL(); if(pbuf->RingBufTxCtr < COMM_TX_BUF_SIZE) { /* See if buffer is full */ full = FALSE; /* Buffer is NOT full */ } else { full = TRUE; /* Buffer is full */ } OS_EXIT_CRITICAL(); return (full); } /* ********************************************************************************************************* * OUTPUT CHARACTER * * * 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. * Arguments : 'ch' is the COMM port channel number and can either be: * COMM1 * COMM2 * 'c' is the character to send. * 'to' is the timeout (in clock ticks) to wait in case the buffer is full. If you * specify a timeout of 0, the function will wait forever for the buffer to empty. * Returns : COMM_NO_ERR if the character was placed in the Tx buffer * COMM_TX_TIMEOUT if the buffer didn't empty within the specified timeout period * COMM_BAD_CH if you specify an invalid channel number ********************************************************************************************************* */ unsigned char CommPutChar(unsigned char ch, unsigned char c, unsigned short to) { unsigned char oserr; COMM_RING_BUF *pbuf; OS_CPU_SR cpu_sr; switch(ch) { /* Obtain pointer to communications channel */ case COM1: pbuf = &Comm1Buf; break; case COM2: pbuf = &Comm2Buf; break; default: return (COMM_BAD_CH); } OSSemPend(pbuf->RingBufTxSem, to, &oserr); /* Wait for space in Tx buffer */ if(oserr == OS_TIMEOUT) { return (COMM_TX_TIMEOUT); /* Timed out, return error code */ } OS_ENTER_CRITICAL(); pbuf->RingBufTxCtr++; /* No, increment character count */ *pbuf->RingBufTxInPtr++ = c; /* Put character into buffer */ if(pbuf->RingBufTxInPtr == &pbuf->RingBufTx[COMM_TX_BUF_SIZE]) { /* Wrap IN pointer */ pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0]; } if(pbuf->RingBufTxCtr == 1) { /* See if this is the first character */ COMEnableTxInt(ch); /* Yes, Enable Tx interrupts */ } OS_EXIT_CRITICAL(); return (COMM_NO_ERR); } /* ********************************************************************************************************* * INSERT CHARACTER INTO RING BUFFER * * * Description : This function is called by the Rx ISR to insert a character into the receive ring buffer. * Arguments : 'ch' is the COMM port channel number and can either be: * COMM1 * COMM2 * 'c' is the character to insert into the ring buffer. If the buffer is full, the * character will not be inserted, it will be lost. ********************************************************************************************************* */ static void COMPutRxChar(unsigned char ch, unsigned char c) { COMM_RING_BUF *pbuf; switch(ch) { /* Obtain pointer to communications channel */ case COM1: pbuf = &Comm1Buf; break; case COM2: pbuf = &Comm2Buf; break; default: return; } if(pbuf->RingBufRxCtr < COMM_RX_BUF_SIZE) { /* See if buffer is full */ pbuf->RingBufRxCtr++; /* No, increment character count */ *pbuf->RingBufRxInPtr++ = c; /* Put character into buffer */ if(pbuf->RingBufRxInPtr == &pbuf->RingBufRx[COMM_RX_BUF_SIZE]) { /* Wrap IN pointer */ pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0]; } OSSemPost(pbuf->RingBufRxSem); /* Indicate that character was received */ } } // This function is called by the Rx ISR to insert a character into the receive ring buffer. void USART1_IRQHandler(void) { unsigned int data; unsigned char err; OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */ OSIntNesting++; OS_EXIT_CRITICAL(); if(USART1->SR & 0x0F) { // See if we have some kind of error // Clear interrupt (do nothing about it!) data = USART1->DR; } else if(USART1->SR & USART_FLAG_RXNE) //Receive Data Reg Full Flag { data = USART1->DR; COMPutRxChar(COM1, data); // Insert received character into buffer } else if(USART1->SR & USART_FLAG_TXE) { data = COMGetTxChar(COM1, &err); // Get next character to send. if(err == COMM_TX_EMPTY) { // Do we have anymore characters to send ? // No, Disable Tx interrupts //USART_ITConfig(USART1, USART_IT_TXE| USART_IT_TC, DISABLE); USART1->CR1 &= ~USART_FLAG_TXE | USART_FLAG_TC; } else { USART1->DR = data; // Yes, Send character } } OSIntExit(); } void USART2_IRQHandler(void) { unsigned int data; unsigned char err; OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */ OSIntNesting++; OS_EXIT_CRITICAL(); if(USART2->SR & 0x0F) { // See if we have some kind of error // Clear interrupt (do nothing about it!) data = USART2->DR; } else if(USART2->SR & USART_FLAG_RXNE) //Receive Data Reg Full Flag { data = USART2->DR; COMPutRxChar(COM2, data); // Insert received character into buffer } else if(USART2->SR & USART_FLAG_TXE) { data = COMGetTxChar(COM2, &err); // Get next character to send. if(err == COMM_TX_EMPTY) { // Do we have anymore characters to send ? // No, Disable Tx interrupts //USART_ITConfig(USART2, USART_IT_TXE| USART_IT_TC, DISABLE); USART2->CR1 &= ~USART_FLAG_TXE | USART_FLAG_TC; } else { USART2->DR = data; // Yes, Send character } } OSIntExit(); }
下面再给出个测试代码:
#include "stm32f10x.h" #include "uart.h" #include "led.h" #include "COMMRTOS.H" #include "ucos_ii.h" #define TASK_STK_SIZE 128 OS_STK TaskStartStk[TASK_STK_SIZE]; OS_STK TaskUartReadStk[TASK_STK_SIZE]; void TaskUartRead(void *pdata) { unsigned char err; unsigned char c; for(;;) { c = CommGetChar(COM2, 0, &err); if(err == COMM_NO_ERR) CommPutChar(COM2, c, 0); } } void TaskStart(void *pdata) { SysTick_Config(SystemCoreClock/10); USART1_Init(); USART2_Init(); COMInit(); //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); USART1->CR1 |= USART_FLAG_RXNE; USART2->CR1 |= USART_FLAG_RXNE; OSTaskCreate(TaskUartRead, (void *)0, &(TaskUartReadStk[TASK_STK_SIZE-1]), 2); UART_PutStr (USART2, "USART2 Hello World!\n\r"); //CommPutChar(COM2, '+', 0); CommPutStr(COM2, "CommPutCharB\n\r"); for(;;) { LED_Spark(); CommPutChar(COM2, '+', 0); OSTimeDly(10); } } int main(void) { SystemInit(); LED_Init(); OSInit(); OSTaskCreate(TaskStart, (void *)0, &(TaskStartStk[TASK_STK_SIZE-1]), 1); OSStart(); for(;;) { } }