Xv6驱动(二):UART串口

阅读材料

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值