串口的使用
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内核采用的是宏内核,因为我是以宏内核的套路来理解微内核,所以导致了这些困惑的产生。
以上是关于串口的使用的主要内容,如果未能解决你的问题,请参考以下文章