在上一篇学习笔记《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
uart.c 的代码
#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);
}
commrtos.h 的代码如下:
#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(;;)
{
}
}