【RT-Thread】nxp rt10xx 设备驱动框架之--uart搭建和使用

UART全称:Universal Asynchronous Receiver-Transmitter, 通用异步收发器。与IIC、SPI 等接口一样,都属于串行通信接口,但是UART只有数据线收和发,无时钟线,故为异步串行通信接口,可以实现全双工通信;在嵌入式系统中,常用与控制系统与外设通信,包括控制器与控制器,控制器与终端设备。线路简单,成本低,传输距离远,但传输速度慢。需要深入了解,网上资料很多,本章不做扩展。

开发前准备

  • 硬件平台:nxp rt10xx单片机
  • IDE: Keil

1.Kconfig 修改和menuconfig配置

Env环境menuconfigRT-Thread Components->Device Drivers 设备驱动默认为y,毕竟最常用的外设之一,在RT-Thread系统中会用到MSH所以,基本都会用到串口。

请添加图片描述

先在Kconfig中添加如下语句,然后在Env环境menuconfigHardware Drivers Config->On-Chip Peripheral Drivers 使能UART,本章使用UART1/2 其中,UART1用于MSHUART2 为本章测试内容。

请添加图片描述
MSH 配置如下:

/* 开启 FinSH */
#define RT_USING_FINSH

/* 将线程名称定义为 tshell */
#define FINSH_THREAD_NAME "tshell"

/* 开启历史命令 */
#define FINSH_USING_HISTORY
/* 记录 5 行历史命令 */
#define FINSH_HISTORY_LINES 5

/* 开启使用 Tab 键 */
#define FINSH_USING_SYMTAB
/* 开启描述功能 */
#define FINSH_USING_DESCRIPTION

/* 定义 FinSH 线程优先级为 20 */
#define FINSH_THREAD_PRIORITY 20
/* 定义 FinSH 线程的栈大小为 4KB */
#define FINSH_THREAD_STACK_SIZE 4096
/* 定义命令字符长度为 80 字节 */
#define FINSH_CMD_SIZE 80

/* 开启 msh 功能 */
#define FINSH_USING_MSH
/* 默认使用 msh 功能 */
#define FINSH_USING_MSH_DEFAULT
/* 最大输入参数数量为 10 个 */
#define FINSH_ARG_MAX 10

请添加图片描述

2.工程添加UART驱动框架和BSP驱动接口

设备驱动框架:serial.c BSP接口:drv_uart.c fsl_lpuart.c

请添加图片描述

3.添加或修改drv_uart.c

串口设备驱动基本是不用改动的,因为上rt-thread系统必定会用到,不过设备驱动底层还是需要了解的

  • 定义uart device
struct serial_configure
{
    rt_uint32_t baud_rate;

    rt_uint32_t data_bits               :4;
    rt_uint32_t stop_bits               :2;
    rt_uint32_t parity                  :2;
    rt_uint32_t bit_order               :1;
    rt_uint32_t invert                  :1;
    rt_uint32_t bufsz                   :16;
    rt_uint32_t flowcontrol             :1;
    rt_uint32_t reserved                :5;
};

/**
 * uart operators
 */
struct rt_uart_ops
{
    rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);
    rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);

    int (*putc)(struct rt_serial_device *serial, char c);
    int (*getc)(struct rt_serial_device *serial);

    rt_size_t (*dma_transmit)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction);
};

struct rt_serial_device
{
    struct rt_device          parent;

    const struct rt_uart_ops *ops;
    struct serial_configure   config;

    void *serial_rx;
    void *serial_tx;
};
  • 设备创建注册
int rt_hw_uart_init(void)
{
    int i;
    rt_uint32_t flag;
    rt_err_t ret = RT_EOK;
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;

    flag = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX;

    uart_get_dma_config();

    for (i = 0; i < sizeof(uarts) / sizeof(uarts[0]); i++)
    {
        uarts[i].serial.ops    = &imxrt_uart_ops;
        uarts[i].serial.config = config;

        ret = rt_hw_serial_register(&uarts[i].serial, uarts[i].name, flag | uarts[i].dma_flag, NULL);
    }

    return ret;
}
INIT_BOARD_EXPORT(rt_hw_uart_init);
  • 基于imxrt uart device 驱动相关函数
uint32_t GetUartSrcFreq(void)
{
    uint32_t freq;

    /* To make it simple, we assume default PLL and divider settings, and the only variable
       from application is use PLL3 source or OSC source */
    if (CLOCK_GetMux(kCLOCK_UartMux) == 0) /* PLL3 div6 80M */
    {
        freq = (CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6U) / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U);
    }
    else
    {
        freq = CLOCK_GetOscFreq() / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U);
    }

    return freq;
}

static rt_err_t imxrt_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct imxrt_uart *uart;
    lpuart_config_t config;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart = rt_container_of(serial, struct imxrt_uart, serial);

    LPUART_GetDefaultConfig(&config);
    config.baudRate_Bps = cfg->baud_rate;

    switch (cfg->data_bits)
    {
    case DATA_BITS_7:
        config.dataBitsCount = kLPUART_SevenDataBits;
        break;

    default:
        config.dataBitsCount = kLPUART_EightDataBits;
        break;
    }

    switch (cfg->stop_bits)
    {
    case STOP_BITS_2:
        config.stopBitCount = kLPUART_TwoStopBit;
        break;
    default:
        config.stopBitCount = kLPUART_OneStopBit;
        break;
    }

    switch (cfg->parity)
    {
    case PARITY_ODD:
        config.parityMode = kLPUART_ParityOdd;
        break;
    case PARITY_EVEN:
        config.parityMode = kLPUART_ParityEven;
        break;
    default:
        config.parityMode = kLPUART_ParityDisabled;
        break;
    }

    config.enableTx = true;
    config.enableRx = true;

    LPUART_Init(uart->uart_base, &config, GetUartSrcFreq());

    return RT_EOK;
}

static rt_err_t imxrt_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct imxrt_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart = rt_container_of(serial, struct imxrt_uart, serial);

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_USING_DMA)
    rt_ubase_t ctrl_arg = (rt_ubase_t)arg;
#endif

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT:
        DisableIRQ(uart->irqn);
        break;

    case RT_DEVICE_CTRL_SET_INT:
        LPUART_EnableInterrupts(uart->uart_base, kLPUART_RxDataRegFullInterruptEnable);
        NVIC_SetPriority(uart->irqn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 4, 0));
        EnableIRQ(uart->irqn);
        break;

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_USING_DMA)
    case RT_DEVICE_CTRL_CONFIG:

        if (RT_DEVICE_FLAG_DMA_RX == ctrl_arg)
        {
            imxrt_dma_rx_config(uart);
        }
        else if (RT_DEVICE_FLAG_DMA_TX == ctrl_arg)
        {
            imxrt_dma_tx_config(uart);
        }
        break;
#endif
    }

    return RT_EOK;
}

static int imxrt_putc(struct rt_serial_device *serial, char ch)
{
    struct imxrt_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart = rt_container_of(serial, struct imxrt_uart, serial);

    LPUART_WriteByte(uart->uart_base, ch);
    while (!(LPUART_GetStatusFlags(uart->uart_base) & kLPUART_TxDataRegEmptyFlag));

    return 1;
}

static int imxrt_getc(struct rt_serial_device *serial)
{
    int ch;
    struct imxrt_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart = rt_container_of(serial, struct imxrt_uart, serial);

    ch = -1;
    if (LPUART_GetStatusFlags(uart->uart_base) & kLPUART_RxDataRegFullFlag)
    {
        ch = LPUART_ReadByte(uart->uart_base);
    }

    return ch;
}

4.搭建应用层demo

底层IO初始化,本章初始化UART1UART2

#ifdef BSP_USING_LPUART1
	IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_12_LPUART1_TX, 0U); 
	IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_13_LPUART1_RX, 0U); 
	IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_12_LPUART1_TX, 0x10B0U); 
	IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_13_LPUART1_RX, 0x10B0U); 	
#endif
#ifdef BSP_USING_LPUART2
    IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_02_LPUART2_TX, 0U);
    IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_03_LPUART2_RX, 0U);
    IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_02_LPUART2_TX, 0x10B0u);
    IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_03_LPUART2_RX, 0x10B0u);
#endif  

串口默认基础配置,详见serial.h

/* Default config for serial_configure structure */
#define RT_SERIAL_CONFIG_DEFAULT           \
{                                          \
    BAUD_RATE_115200, /* 115200 bits/s */  \
    DATA_BITS_8,      /* 8 databits */     \
    STOP_BITS_1,      /* 1 stopbit */      \
    PARITY_NONE,      /* No parity  */     \
    BIT_ORDER_LSB,    /* LSB first sent */ \
    NRZ_NORMAL,       /* Normal mode */    \
    RT_SERIAL_RB_BUFSZ, /* Buffer size */  \
    RT_SERIAL_FLOWCONTROL_NONE, /* Off flowcontrol */ \
    0                                      \
}
/**************************************************START OF FILE*****************************************************/






/*------------------------------------------------------------------------------------------------------------------
Includes
*/
#include <rtthread.h>
#include <rtdevice.h>

//#include "serial.h"


/*------------------------------------------------------------------------------------------------------------------
Macros
*/
#define SAMPLE_UART_NAME       "uart2"    /* 串口设备名称 */

/*------------------------------------------------------------------------------------------------------------------
Variables
*/
static rt_device_t serial;                /* 串口设备句柄 */
struct serial_configure uart2config = RT_SERIAL_CONFIG_DEFAULT;  /* 初始化配置参数 */

char sendStr[] = "hello uart2!\r\n";

/*------------------------------------------------------------------------------------------------------------------
Functions
*/
void uart_sample(void)
{
    /* step1:查找串口设备 */
    serial = rt_device_find(SAMPLE_UART_NAME);

    /* step2:修改串口配置参数 */
    uart2config.data_bits = DATA_BITS_8;           //数据位 8
    uart2config.stop_bits = STOP_BITS_1;           //停止位 1
    uart2config.bufsz     = 128;                   //修改缓冲区 buff size 为 128
    uart2config.parity    = PARITY_NONE;           //无奇偶校验位

    /* step3:控制串口设备。通过控制接口传入命令控制字,与控制参数 */
    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &uart2config);

    /* step4:打开串口设备。以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);

    /* 发送字符串 */
    rt_device_write(serial, 0, sendStr, (sizeof(sendStr) - 1));  //uart2 发送参数

    rt_kprintf("hello uart1!\n");
}
/* 导出到 msh 命令列表中 */ 
MSH_CMD_EXPORT(uart_sample, uart sample); 
/****************************************************END OF FILE*****************************************************/


将两个串口外接到终端上(笔者使用的是MobaXterm,极力推荐这个多功能终端神器),打开两个窗口。复位MCU,输入uart_sample,在两个窗口上将分别看到:hello uart1hello uart2

请添加图片描述

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值