串口驱动设计

文章目录

1 串口驱动设计

串口发送或者接收,各自实现了一个"管道"。对于任意管道,一端写入数据,另一端读取数据。或者可以理解为一方生产,一方消费,即所谓的"生产-消费者模型"。
在这里插入图片描述

以串口数据发送为例,该驱动完成任务与发送中断之间的通信。

通信通过自定义的全局数据结构来实现,待发送的数据写入到循环FIFO中。然后结合信号量来实现读写的同步,使用开关中断来对全局数据的访问进行共享保护控制。

串口数据的接收,工作原理和发送类似。

可以看到,无论是发送还是接受,通过RTOS的这些功能模块能够有效地提升数据收发效率。

实现代码如下:
uart.h:

/**
 * @brief 串口驱动
 * @details
 * @author 01课堂 李述铜 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版权所有,禁止用于商业用途
 */
#ifndef UART_H
#define UART_H

#define UART_BAUDRATE           115200
#define UART_RXBUFFER_SIZE      32
#define UART_TXBUFFER_SIZE      128

void UartInit (void);
void UartRead (char * packet, uint32_t len);
void UartWrite (const char * packet, uint32_t len);

#endif //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));
}


参考资料:

  1. 手把手教你学用嵌入式操作系统
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值