串口驱动设计
文章目錄
- 1 串口驅動設計
1 串口驅動設計
串口發送或者接收,各自實現了一個"管道"。對于任意管道,一端寫入數據,另一端讀取數據。或者可以理解為一方生產,一方消費,即所謂的"生產-消費者模型"。
以串口數據發送為例,該驅動完成任務與發送中斷之間的通信。
通信通過自定義的全局數據結構來實現,待發送的數據寫入到循環FIFO中。然后結合信號量來實現讀寫的同步,使用開關中斷來對全局數據的訪問進行共享保護控制。
串口數據的接收,工作原理和發送類似。
可以看到,無論是發送還是接受,通過RTOS的這些功能模塊能夠有效地提升數據收發效率。
實現代碼如下:
uart.h:
uart.c:
/*** @brief 串口驅動* @details* @author 01課堂 李述銅 http://01ketang.cc* @date 2017-06-01* @version 1.0* @copyright 版權所有,禁止用于商業用途*/ #include "tinyOS.h" #include "uart.h" #include "stm32f10x_usart.h"static uint8_t rxBuffer[UART_RXBUFFER_SIZE]; static uint32_t rxWritePos; static uint32_t rxReadPos; static tSem rxReceivedSem;static uint8_t txBuffer[UART_TXBUFFER_SIZE]; static uint32_t txWritePos; static uint32_t txReadPos; static tSem txFreeSem;/*** 中斷處理函數*/ void USART1_IRQHandler (void) {ITStatus status;// 串口接收status = USART_GetITStatus(USART1, USART_IT_RXNE);if (status == SET) {tSemInfo semInfo;uint16_t ch = (uint32_t)USART_ReceiveData(USART1);// 僅當有空閑空間時才寫入,否則丟棄tSemGetInfo(&rxReceivedSem, &semInfo);if (semInfo.count < semInfo.maxCount) {tTaskCritical_t critical = tTaskEnterCritical();rxBuffer[rxWritePos++] = (uint8_t)ch;if (rxWritePos >= UART_RXBUFFER_SIZE) {rxWritePos = 0;}tTaskExitCritical(critical);tSemNotify(&rxReceivedSem);}USART_ClearITPendingBit(USART1, USART_IT_RXNE);}// 發送中斷: 自動從郵箱中取數據發送status = USART_GetITStatus(USART1, USART_IT_TXE);if (status == SET) {tSemInfo semInfo;// 如果發送緩沖有數據,取一個發送tSemGetInfo(&txFreeSem, &semInfo);if (semInfo.count < semInfo.maxCount) {tTaskCritical_t critical;uint8_t ch;// 從發送緩沖區中取數據critical = tTaskEnterCritical();ch = txBuffer[txReadPos++];if (txReadPos >= UART_TXBUFFER_SIZE) {txReadPos = 0;}tTaskExitCritical(critical);tSemNotify(&txFreeSem);USART_SendData(USART1, (uint16_t)ch);} else {USART_ITConfig(USART1, USART_IT_TXE, DISABLE);}USART_ClearITPendingBit(USART1, USART_IT_TXE); } }/*** USART硬件初始化*/ static void UartHalInit (void) {USART_InitTypeDef USART_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);USART_InitStructure.USART_BaudRate = UART_BAUDRATE;USART_InitStructure.USART_WordLength = USART_WordLength_9b;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);USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_EnableIRQ(USART1_IRQn); }/*** 初始化串口*/ void UartInit (void) {UartHalInit();tSemInit(&rxReceivedSem, 0, UART_RXBUFFER_SIZE);rxReadPos = 0;rxWritePos = 0;tSemInit(&txFreeSem, UART_TXBUFFER_SIZE, UART_TXBUFFER_SIZE);txReadPos = 0;txWritePos = 0;}/*** 等待接收數據包* @param packet 接收到的數據包指針存儲地址* @return 0 無錯誤;1 有錯誤*/ void UartRead (char * packet, uint32_t len) {tTaskCritical_t critical;while (len -- > 0) {tSemWait(&rxReceivedSem, 0);// 從接收緩沖區中讀取critical = tTaskEnterCritical();*packet++ = rxBuffer[rxReadPos++];if (rxReadPos >= UART_RXBUFFER_SIZE) {rxReadPos = 0;}tTaskExitCritical(critical);} }/*** 寫入數據包* @param packet 待寫入的數據包*/ void UartWrite (const char * packet, uint32_t len) {tTaskCritical_t critical;uint32_t status;while (len-- > 0) {// 等待空閑空間tSemWait(&txFreeSem, 0);// 寫入發送緩沖區critical = tTaskEnterCritical();txBuffer[txWritePos++] = *packet++;if (txWritePos >= UART_TXBUFFER_SIZE) {txWritePos = 0;}tTaskExitCritical(critical);// 這里加循環反復調用,是考慮到中途可能發生其它中斷延遲導致沒有及時觸發// 只有當硬件真正在空閑時,才手動觸發一次status = USART_GetFlagStatus(USART1, USART_FLAG_TXE);if (status == SET) {USART_ITConfig(USART1, USART_IT_TXE, ENABLE);}} }app.c:
/*** @brief tOS應用示例* @details* @author 01課堂 李述銅 http://01ketang.cc* @date 2017-06-01* @version 1.0* @copyright 版權所有,禁止用于商業用途*/ #include "tinyOS.h" #include "app.h" #include "hal.h"#include "button.h" #include "uart.h"static tTaskStack task1Env[TASK1_ENV_SIZE]; // 任務1的堆棧空間 static tTask task1;void task1Entry (void * param) {char str[64];uint32_t count = 0;for (;;) { // sprintf(str, "Button press: %d\n", count++); // UartWritePacket(str);char msg;UartRead(&msg, 1);UartWrite(&msg, 1);} }/*** App的初始化*/ void tInitApp (void) {halInit();ButtonInit();UartInit();tTaskInit(&task1, task1Entry, (void *) 0x0, TASK1_PRIO, task1Env, sizeof(task1Env)); }參考資料:
總結
- 上一篇: bt3u盘怎么进入 bt3u盘如何使用
- 下一篇: 外部IO驱动设计