Xv6驱动(二):UART串口

2024-08-26 05:44
文章标签 驱动 串口 uart xv6

本文主要是介绍Xv6驱动(二):UART串口,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

阅读材料

  • Xv6代码:uart.c
  • 教材第5章
  • 课程视频Lecture9-Interrupts 
  • 16550 UART手册:ByteRunner.com | TECHNICAL DATA ON 16550

UART寄存器布局

#define RHR 0 // receive holding register (for input bytes)
#define THR 0 // transmit holding register (for output bytes)
#define IER 1 // interrupt enable register
#define IER_RX_ENABLE (1 << 0)
#define IER_TX_ENABLE (1 << 1)
#define FCR 2 // FIFO control register
#define FCR_FIFO_ENABLE (1 << 0)
#define FCR_FIFO_CLEAR (3 << 1) // clear the content of the two FIFOs
#define ISR 2					// interrupt status register
#define LCR 3					// line control register
#define LCR_EIGHT_BITS (3 << 0)
#define LCR_BAUD_LATCH (1 << 7) // special mode to set baud rate
#define LSR 5					// line status register
#define LSR_RX_READY (1 << 0)	// input is waiting to be read from RHR
#define LSR_TX_IDLE (1 << 5)	// THR can accept another character to send

UART每个寄存器栈1byte,寄存器布局可以参考手册 

 UART驱动

uartinit函数

函数调用链条:main()--->consoleinit()--->uartinit()

UART是整个内核第一个初始化的组件,因为初始化UART以后,就可以调用printf函数进行输出了

  1. 关闭UART中断,因为内核初始化UART时不希望被UART的中断打断
  2. 设置波特率
  3. 设置传输的word的长度为8bits
  4. 情况并使能FIFO,注意这个FIFO是UART内部的,对程序员不可见
  5. 使能中断
  6. 初始化锁
void uartinit(void)
{// disable interrupts.WriteReg(IER, 0x00);// special mode to set baud rate.WriteReg(LCR, LCR_BAUD_LATCH);// LSB for baud rate of 38.4K.WriteReg(0, 0x03);// MSB for baud rate of 38.4K.WriteReg(1, 0x00);// leave set-baud mode,// and set word length to 8 bits, no parity.WriteReg(LCR, LCR_EIGHT_BITS);// reset and enable FIFOs.WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);// enable transmit and receive interrupts.WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);initlock(&uart_tx_lock, "uart");
}

uartputc_sync函数

这个函数只会被内核panic函数调用,panic函数是当内核发生严重错误时调用,打印出错信息。

当panic函数被调用时,会设置panicked = 1,这样当其他核心调用printf/panic函数时,会陷入死循环,起到了阻塞输出的作用

该函数会等待THR寄存器为空,然后将字符写入该寄存器中。

void uartputc_sync(int c)
{push_off();if (panicked){for (;;);}// wait for Transmit Holding Empty to be set in LSR.while ((ReadReg(LSR) & LSR_TX_IDLE) == 0);WriteReg(THR, c);pop_off();
}

 uartputc函数

函数调用链条:write()--->consolewrite()--->uartputc()

  1. 判断缓冲区是否已满,如果满了的化调用sleep函数
  2. 否则,将字符放入缓冲区中,并更新缓冲区索引
  3. 调用uartstart函数开始执行UART字符传输
void uartputc(int c)
{acquire(&uart_tx_lock);if (panicked){for (;;);}while (uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){// buffer is full.// wait for uartstart() to open up space in the buffer.sleep(&uart_tx_r, &uart_tx_lock);}uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c;uart_tx_w += 1;uartstart();release(&uart_tx_lock);
}

uartstart函数

  1. 首先判断THR是否为满,如果满的化会直接返回
  2. 移动uart_tx_r索引,标识已经发送一个字符
  3. 唤醒其他因为缓存区满而sleep的进程
  4. 发送该字符
void uartstart()
{while (1){if (uart_tx_w == uart_tx_r){// transmit buffer is empty.ReadReg(ISR);return;}if ((ReadReg(LSR) & LSR_TX_IDLE) == 0){// the UART transmit holding register is full,// so we cannot give it another byte.// it will interrupt when it's ready for a new byte.return;}int c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE];uart_tx_r += 1;// maybe uartputc() is waiting for space in the buffer.wakeup(&uart_tx_r);WriteReg(THR, c);}
}

uartgetc函数

该函数首先判断RHR寄存器是否有字符,有的化读取并返回该字符;否则返回-1

int uartgetc(void)
{if (ReadReg(LSR) & 0x01){// input data is ready.return ReadReg(RHR);}else{return -1;}
}

uartintr函数

有两种情况UART会产生中断

  1. 有新的字符到达RHR寄存器
  2. THR发送完毕,现在THR已经空了,可以接受新的字符

该函数会判断这两种情况,并调用相应的函数来处理

void uartintr(void)
{// read and process incoming characters.while (1){int c = uartgetc();if (c == -1)break;consoleintr(c);}// send buffered characters.acquire(&uart_tx_lock);uartstart();release(&uart_tx_lock);
}

参考资料

The xv6 Kernel-18 uart.c and console.c_哔哩哔哩_bilibili

The xv6 Kernel-18 uart.c and console.c_哔哩哔哩_bilibili

这篇关于Xv6驱动(二):UART串口的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1107711

相关文章

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

驱动(RK3588S)第七课时:单节点设备树

目录 需求一、设备树的概念1、设备树的后缀名:2、设备树的语法格式3、设备树的属性(重要)4、设备树格式举例 二、设备树所用函数1、如何在内核层种获取设备树节点:2、从设备树上获取 gpio 口的属性3、获取节点上的属性只针对于字符串属性的4、函数读取 np 结点中的 propname 属性的值,并将读取到的 u32 类型的值保存在 out_value 指向的内存中,函数的返回值表示读取到的

驱动安装注册表指令

HKCR: HKEY_CLASSES_ROOT HKCU: HKEY_CURRENT_USER HKLM: HKEY_LOCAL_MACHINE HKU: HEKY_USER HER: 相对根键

UMDF驱动安装

VS2013 + WDF8.1,UMDF驱动选择User Mode Driver,不要选User Mode Driver 2.0,否则Win7安装有问题,如图 另外,在驱动安装时不要忘记WUDFUpdate_<主版本号><次版本号>.dll文件,具体文件名在INF中查找。此文件可在WDF的安装目录中找到。注意:在WDF的安装目录中会有3个WUDFUpdate_xxx.dll文件,x86,x6

安卓开发板_联发科MTK开发评估套件串口调试

串口调试 如果正在进行lk(little kernel ) 或内核开发,USB 串口适配器( USB 转串口 TTL 适配器的简称)对于检查系统启动日志非常有用,特别是在没有图形桌面显示的情况下。 1.选购适配器 常用的许多 USB 转串口的适配器,按芯片来分,有以下几种: CH340PL2303CP2104FT232 一般来说,采用 CH340 芯片的适配器,性能比较稳定,价

VB和51单片机串口通信讲解(只针对VB部分)

标记:该篇文章全部搬自如下网址:http://www.crystalradio.cn/thread-321839-1-1.html,谢谢啦            里面关于中文接收的部分,大家可以好好学习下,题主也在研究中................... Commport;设置或返回串口号。 SettingS:以字符串的形式设置或返回串口通信参数。 Portopen:设置或返回串口

电脑驱动分类

电脑驱动程序(驱动程序)是操作系统与硬件设备之间的桥梁,用于使操作系统能够识别并与硬件设备进行通信。以下是常见的驱动分类: 1. 设备驱动程序 显示驱动程序:控制显卡和显示器的显示功能,负责图形渲染和屏幕显示。 示例:NVIDIA、AMD 显示驱动程序。打印机驱动程序:允许操作系统与打印机通信,控制打印任务。 示例:HP、Canon 打印机驱动程序。声卡驱动程序:管理音频输入和输出,与声卡硬件

麒麟系统安装GPU驱动

1.nvidia 1.1显卡驱动 本机显卡型号:nvidia rtx 3090 1.1.1下载驱动 打开 https://www.nvidia.cn/geforce/drivers/ 也可以直接使用下面这个地址下载 https://www.nvidia.com/download/driverResults.aspx/205464/en-us/ 1.1.3安装驱动 右击,

windows10 卸载网络驱动以及重新安装

右键桌面此电脑的图标,点击管理,设备管理器—网络适配器,找到下图中的驱动(不同的系统或者显卡会导致网卡驱动名称与下图不一样,多为Realtek开头),右键选择卸载设备,然后重启电脑,系统会自动重新安装驱动 新电脑首次安装驱动: 根据主板厂家,比如华硕,进入华硕官网,点击服务支持,点击下载中心,选择型号,点击右侧驱动程序和工具软件,选择windows版本,下载相应的驱动,下载完之后在对应文件中找

笔记整理—内核!启动!—kernel部分(1)驱动与内核的关系

首先,恭喜完成了uboot部分的内容整理,其次补充一点,uboot第一部分和第二部分的工作不是一定的,在不同的版本中,可能这个初始化早一点,那个的又放在了第二部分,版本不同,造成的工作顺序不同,但终归是要完成基本内容初始化并传参给kernel的。         那么至于驱动与内核的关系,用一张图来说明最适合不过:         驱动位于OS层的中下层与硬件相接。驱动是内