串口的使用

Posted 四季帆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了串口的使用相关的知识,希望对你有一定的参考价值。

0. 前言

        本篇文章是通过学习韦东山老师的免费教程整理和总结的,声明一下来源,支持韦东山老师的成果。

1. 内核里打印

        main()函数中就有打印语句,那就从这里开始分析

PRINT_RELEASE("\\nmain core booting up...\\n");

//OpenHarmony/kernel/liteos_a/kernel/include/los_printf.h
#define PRINT_RELEASE(fmt, args...)  LOS_LkPrint(LOS_COMMON_LEVEL, __FUNCTION__, __LINE__, fmt, ##args)

/OpenHarmony/kernel/liteos_a/kernel/common/los_printf.c
VOID LOS_LkPrint(INT32 level, const CHAR *func, INT32 line, const CHAR *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    OsVprintf(fmt, ap, CONSOLE_OUTPUT);
    va_end(ap);
}
VOID OsVprintf(const CHAR *fmt, va_list ap, OutputType type)
{
    len = vsnprintf_s(bBuf, bufLen, bufLen - 1, fmt, ap);
    while (len == -1) {
        bufLen = bufLen << 1;
        if ((INT32)bufLen <= 0) {
            //发送字符串
            UartPuts(errMsgLen, (UINT32)strlen(errMsgLen), UART_WITH_LOCK);
            return;
        }
        bBuf = (CHAR *)LOS_MemAlloc(m_aucSysMem0, bufLen);
        len = vsnprintf_s(bBuf, bufLen, bufLen - 1, fmt, ap);
        if (*bBuf == '\\0') {
                ·····
        }
    }
}

/OpenHarmony/kernel/liteos_a/platform/uart/amba_plo11/amba_plo11.c
VOID UartPuts(const CHAR *s, UINT32 len, BOOL isLock)
{
    UINTPTR base = uart_to_ptr(0);
    (VOID)UartPutsReg(base, s, len, isLock);  //发送字符串
}
UINT32 UartPutsReg(UINTPTR base, const CHAR *s, UINT32 len, BOOL isLock)
{
    if (isLock) {
        LOS_SpinLockSave(&g_uartOutputSpin, &intSave);
        UartPutStr(base, s, len);    //发送字符串
        LOS_SpinUnlockRestore(&g_uartOutputSpin, intSave);
    } else {
        UartPutStr(base, s, len);
    }
    return len;
}
STATIC VOID UartPutStr(UINTPTR base, const CHAR *s, UINT32 len)
{
    UINT32 i;
    for (i = 0; i < len; i++) {
        if (*(s + i) == '\\n') {
            UartPutcReg(base, '\\r');   //循环发送单个字符
        }
        UartPutcReg(base, *(s + i));
    }
}
STATIC VOID UartPutcReg(UINTPTR base, CHAR c)
{
    /* Spin while fifo is full */
    while (UARTREG(base, UART_FR) & UART_FR_TXFF) {}
    UARTREG(base, UART_DR) = c;     //直接往地址上写字符
}
#define UARTREG(base, reg)  (*REG32((base) + (reg)))

2. 作为APP控制台

2.1 串口设备资源的注册(寄存器基地址、中断号等设备资源)

OsMain()
    --->OsBsdInit()
        --->configure()
            --->nexus_init()
                --->machine_resource_init(add_resource)
                    --->uart_add_device(callback);

void machine_resource_init(add_res_callback_t callback)
{
    if (callback == NULL) {
        return;
    }
    uart_add_device(callback);
    imx6ull_spinor_resource_init(callback);
}
static void uart_add_device(add_res_callback_t callback)
{
    device_t uart_dev;
    UART_ADD_DEVICE(uart_dev, 0);
    callback("uart", SYS_RES_MEMORY, 0, UART0_REG_PBASE, UART0_REG_PBASE + UART_IOMEM_COUNT, UART_IOMEM_COUNT);
    callback("uart", SYS_RES_IRQ, 0, NUM_HAL_INTERRUPT_UART0, NUM_HAL_INTERRUPT_UART0, 1);
    //这里的callback回调函数即add_resource
}
static void add_resource(const char *devclass_name, int type, int unit, rman_res_t start, rman_res_t end, rman_res_t count)
{
	struct resource_list *res_list = get_resource_list(devclass_name);
	if (res_list == NULL) {
		return;
	}
	resource_list_add(res_list, type, unit, start, end, count);    /* 将设备资源管理起来 */
}

2.2 串口设备驱动的注册

SystemInit()
    --->uart_dev_init()
        --->driver_module_handler(NULL, MOD_LOAD, &uart_nexus_driver_mod)

//这里是韦东山老师移植的imx6ull板子对应的SystemInit()函数实现
void SystemInit()
{
#ifdef LOSCFG_FS_PROC
    dprintf("proc fs init ...\\n");
    extern void ProcFsInit(void);
    ProcFsInit();
#endif

    imx6ull_driver_init();
    imx6ull_mount_rootfs();

    extern int uart_dev_init(void);
    uart_dev_init();       /* --> */

    if (virtual_serial_init("/dev/uartdev-0") != 0)
    {
        PRINT_ERR("virtual_serial_init failed");
    }
    if (system_console_init(SERIAL) != 0)
    {
        PRINT_ERR("system_console_init failed\\n");
    }
    if (OsUserInitProcess())
    {
        PRINT_ERR("Create user init process faialed!\\n");
    }
}
---------------------------------------------------------------------------
static device_method_t uart_methods[] = 
{
	/* Device interface */
	DEVMETHOD(device_probe, imx6ull_probe),      //1. --->
	DEVMETHOD(device_attach, imx6ull_attach),    //2. --->
	DEVMETHOD(device_detach, imx6ull_detach),
	DEVMETHOD(device_shutdown, bus_generic_shutdown),
	DEVMETHOD_END
};
static driver_t uart_driver = 
{
	.name = "uart",
	.methods = uart_methods,
	.size = sizeof(struct uart_softc),
};
static devclass_t uart_devclass;
DRIVER_MODULE(uart, nexus, uart_driver, uart_devclass, 0, 0);
int uart_dev_init(void)
{
	return driver_module_handler(NULL, MOD_LOAD, &uart_nexus_driver_mod);
   //uart_nexus_driver_mod是由DRIVER_MODULE宏生成的
}
//串口设备和串口驱动通过名字匹配以后,会执行imx6ull_probe函数,执行完之后会调用驱动的imx6ull_attach函数

static int imx6ull_probe(device_t self)
{
	return (BUS_PROBE_DEFAULT);
}

static int imx6ull_attach(device_t self)
{
	struct resource *res = NULL;
	char dev_name[MAX_DEV_NAME_SIZE];
	struct imx6ull_port *port = NULL;
	int unit = device_get_unit(self);
	struct uart_softc *sc = device_get_softc(self);
	struct uart_driver_data *udd = sc->udd;

	port = (struct imx6ull_port *)LOS_MemAlloc(m_aucSysMem0, sizeof(struct imx6ull_port));
	memset_s(port, sizeof(struct imx6ull_port), 0, sizeof(struct imx6ull_port));
	res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &unit, 0);
   //物理地址映射到虚拟地址
	port->phys_base = (unsigned long)(uintptr_t)ioremap(res->start, res->count);
	res = bus_alloc_resource_any(self, SYS_RES_IRQ, &unit, 0);
	port->irq_num = res->start;
	udd->private = port;
	udd->ops = &imx6ull_uops;
	port->udd = udd;
	udd->recv = uart_recv_notify;
	udd->count = 0;
	memset_s(dev_name, MAX_DEV_NAME_SIZE, 0, MAX_DEV_NAME_SIZE);
	snprintf_s(dev_name, MAX_DEV_NAME_SIZE, MAX_DEV_NAME_SIZE - 1, "/dev/uartdev-%d", udd->num);
	if (register_driver(dev_name, &uartdev_fops, 0666, udd)) {  /* 注册driver,uartdev_fops是关键 */
		uart_error("gen /dev/uartdev-%d fail!\\n", udd->num);
		goto err;
	}
	return 0;
}

int register_driver(FAR const char *path, FAR const struct file_operations_vfs *fops,
                    mode_t mode, FAR void *priv)
{
  FAR struct inode *node;
  int ret;
  if (ret >= 0)
    { 
      INODE_SET_DRIVER(node);
      /* 将操作串口硬件的函数集放到node->u.i_ops,上层在进行open/read/write操作时通过node->u.i_ops来操控硬件 */
      node->u.i_ops   = fops;      
      node->i_private = priv;
      ret             = OK;
    }
  return ret;
}

const struct file_operations_vfs uartdev_fops = {
    .open   = uartdev_open,
    .close  = uartdev_release,
    .read   = uartdev_read,
    .write  = uartdev_write,
    .seek   = NULL,
    .ioctl  = uartdev_ioctl,
    .mmap   = uartdev_map,
#ifndef CONFIG_DISABLE_POLL
    .poll   = uartdev_poll,
#endif
    .unlink = NULL,
}

2.3 控制台

SystemInit()
    --->system_console_init(SERIAL)
        --->OsConsoleCreate((UINT32)consoleID, deviceName)
            --->OsConsoleDevInit(consoleCB, deviceName)
                --->register_driver(consoleCB->name, &g_consoleDevOps, DEFFILEMODE, filep)

STATIC const struct file_operations_vfs g_consoleDevOps = {
    .open = ConsoleOpen,   /* open */
    .close = ConsoleClose, /* close */
    .read = ConsoleRead,   /* read */
    .write = ConsoleWrite, /* write */
    .seek = NULL,
    .ioctl = ConsoleIoctl,
    .mmap = NULL,
#ifndef CONFIG_DISABLE_POLL
    .poll = ConsolePoll,
#endif
    .unlink = NULL,
};

STATIC INT32 ConsoleOpen(struct file *filep)
{
    INT32 ret;
    UINT32 consoleID;
    struct file *privFilep = NULL;
    const struct file_operations_vfs *fileOps = NULL;

    consoleID = (UINT32)OsConsoleFullpathToID(filep->f_path);
    filep->f_priv = g_console[consoleID - 1];
    
    ret = GetFilepOps(filep, &privFilep, &fileOps);   /* 根据filep找到fileOps */
    ret = FilepOpen(privFilep, fileOps);     /* 调用fileOps->open */
    return ENOERR;
}
     
INT32 GetFilepOps(const struct file *filep, struct file **privFilep, const struct file_operations_vfs **filepOps)
{
    INT32 ret;
    /* to find uart driver operation function throutht u.i_opss */
    *filepOps = (*privFilep)->f_inode->u.i_ops;
    /* 问题1:这里取出的是g_consoleDevOps还是uartdev_fops,有待商榷,至少从源码上看不出来,这得大佬才能搞了!!! */
    return ENOERR;
}

INT32 FilepOpen(struct file *filep, const struct file_operations_vfs *fops)
{
    INT32 ret;
   
    /* adopt uart open function to open filep (filep is corresponding to filep of /dev/console) */
    ret = fops->open(filep);
    return ret;
}

2.4 总结

        老实说,我觉得这个串口驱动的分层有点儿模糊,所以这就导致了问题1 的出现;还有一个问题是,驱动和设备的匹配动作是在哪里实现的?从代码上还没看到实现。

        我猜测这些问题和鸿蒙的架构有一定关系,因为鸿蒙内核采用的是微内核,而Linux内核采用的是宏内核,因为我是以宏内核的套路来理解微内核,所以导致了这些困惑的产生。

以上是关于串口的使用的主要内容,如果未能解决你的问题,请参考以下文章

TQ2440开发板学习纪实--- 基于中断的UART串口接收

STM32F10x 串口使用DMA

QT5 串口收发实例代码

QT串口不工作

Linux基础(串口编程)

STM32 USB使用记录:使用CDC类虚拟串口(VCP)进行通讯