当前位置: 首页 > news >正文

RVOS-2.基于NS16550a ,为os添加终端交互功能。

2.1 实验目的

为os添加uart功能,通过串口实现开发板与PC交互。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.1 硬件信息

QEMU虚拟SoC含有 虚拟NS16550A设备 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不同的地址线组合(A2、A1、A0)对应的读写模式和寄存器如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.2 NS16550a 的初始化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

线路控制寄存器(LCR)中的bit7位来实现复用DLL、DLM两个寄存器拼起来作为16位波特率寄存器。当bit7位被设置为1时,地址0和1用于访问除数锁存寄存器(DLL和DLM),用于设置波特率。

  • 关闭中断
  • 设置波特率
  • 设置异步数据通信格式
void uart_init()
{
	/* disable interrupts. */
	uart_write_reg(IER, 0x00);

	/*
	 * Setting baud rate. Just a demo here if we care about the divisor,
	 * but for our purpose [QEMU-virt], this doesn't really do anything.
	 *
	 * Notice that the divisor register DLL (divisor latch least) and DLM (divisor
	 * latch most) have the same base address as the receiver/transmitter and the
	 * interrupt enable register. To change what the base address points to, we
	 * open the "divisor latch" by writing 1 into the Divisor Latch Access Bit
	 * (DLAB), which is bit index 7 of the Line Control Register (LCR).
	 *
	 * Regarding the baud rate value, see [1] "BAUD RATE GENERATOR PROGRAMMING TABLE".
	 * We use 38.4K when 1.8432 MHZ crystal, so the corresponding value is 3.
	 * And due to the divisor register is two bytes (16 bits), so we need to
	 * split the value of 3(0x0003) into two bytes, DLL stores the low byte,
	 * DLM stores the high byte.
	 */
    
	uint8_t lcr = uart_read_reg(LCR);
	uart_write_reg(LCR, lcr | (1 << 7));
	uart_write_reg(DLL, 0x03);
	uart_write_reg(DLM, 0x00);

	/*
	 * Continue setting the asynchronous data communication format.
	 * - number of the word length: 8 bits
	 * - number of stop bits:1 bit when word length is 8 bits
	 * - no parity
	 * - no break control
	 * - disabled baud latch
	 */
	lcr = 0;
	uart_write_reg(LCR, lcr | (3 << 1));
}

原代码这里是这样,感觉不太对,应该是左移一位的。

lcr = 0;
uart_write_reg(LCR, lcr | (3 << 0));

2.3 NS16550a 的数据读写

在NS16550A UART中,区分读写模式是通过控制信号(如读/写控制线)来实现的,而不是通过寄存器地址。这些控制信号通常由CPU或其他主控设备提供。以下是区分读写模式的一般步骤:

  1. 当CPU或其他主控设备想要从UART读取数据时,它会将读控制线置为有效状态(低电平)。同时将芯片选择信号置为有效状态,以选中UART设备。
  2. 当CPU或其他主控设备想要向UART写入数据时,它会将写控制线置为有效状态(低电平)。同样将芯片选择信号置为有效状态,以选中UART设备。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

读:

/*
 * LINE STATUS REGISTER (LSR)
 * LSR BIT 0:
 * 0 = no data in receive holding register or FIFO.
 * 1 = data has been receive and saved in the receive holding register or FIFO.
 * ......
 * LSR BIT 5:
 * 0 = transmit holding register is full. 16550 will not accept any data for transmission.
 * 1 = transmitter hold register (or FIFO) is empty. CPU can load the next character.
 * ......
 */
#define LSR_RX_READY (1 << 0)
#define LSR_TX_IDLE  (1 << 5)

int uart_putc(char ch)
{
	while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0);
	return uart_write_reg(THR, ch);
}

void uart_puts(char *s)
{
	while (*s) {
		uart_putc(*s++);
	}
}

写:

练习 7-2

要求:参考code/os/01-helloRVOS,在此基础上增加采⽤轮询⽅式读取控制台上输入的字符并 回显 在控制台上。另外⽤户按下回⻋后能够另起⼀⾏从头开始。

int uart_getc()
{
    char ch;

    while ((uart_read_reg(LSR) & LSR_RX_READY) == 0);
    ch = uart_read_reg(RHR);
    return ch;
}


void uart_gets(char *s, int len)
{
    int i = 0;
    char ch;

    while (i < len - 1) {
        ch = uart_getc(); 
        if (ch == '\r') { 
            break;
        }
        s[i++] = ch;
    }
    s[i] = '\0'; 
}

/**
 * 回显功能:读取用户输入并回显到控制台
 */
void uart_echo()
{
    char buffer[100]; 	

    uart_puts("UART Echo Ready:\r\n");
    while (1) {
        uart_gets(buffer, sizeof(buffer)); 
        uart_putc('\r'); 
        uart_putc('\n'); 
		uart_puts("--kernel收到数据--\n");
        uart_puts(buffer);
        uart_putc('\r'); 
        uart_putc('\n'); 
    }
}

最后一定记得在kernel.c添加extern声明:

extern void uart_echo(void);


void start_kernel(void)
{
	uart_init();
	uart_puts("Hello, RVOS!\n");
	uart_echo();  // 开始回显
	while (1) {}; // stop here!
}

运行结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

存在一个问题就是在终端输入的内容无法显示,且无法删除。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


问题解决:在当前实现中,输入的字符虽然被回显,但无法正确处理删除键(Backspace)的功能。这是因为 uart_gets 函数没有对删除键 (‘\b’ 或 ASCII 8) 进行处理。以下是改进方案:

我们需要在 uart_gets 中添加对删除键的处理逻辑。当用户按下删除键时,应该从缓冲区中移除最后一个字符,并在终端上删除回显的字符。

void uart_gets(char *s, int len)
{
    int i = 0;
    char ch;

    while (i < len - 1) {
        ch = uart_getc(); // 读取一个字符

        if (ch == '\r') { // 如果是回车符,结束读取
            break;
        } else if (ch == '\b' || ch == 127) { // 处理删除键('\b' 或 ASCII 127)
            if (i > 0) {
                i--; // 从缓冲区中移除最后一个字符
                uart_putc('\b'); // 回显删除键
                uart_putc(' ');  // 用空格覆盖已删除的字符
                uart_putc('\b'); // 将光标移回一格
            }
        } else {
            s[i++] = ch; 	// 存储字符
            uart_putc(ch); 	// 回显输入的字符
        }
    }
    s[i] = '\0'; // 添加字符串结束符
}

问题完美解决!!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【[完结] 循序渐进,学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春】 https://www.bilibili.com/video/BV1Q5411w7z5/?p=19&share_source=copy_web&vd_source=d63943fdb26087d14a536adf35c52d6b

相关文章:

  • 车载刷写架构 --- ECU收到相同的blockSequenceCounter数据包的思考
  • Java Collections 类中常用方法使用
  • Elasticsearch 系列专题 - 第二篇:数据建模与索引管理
  • Traefik应用:配置容器多个网络时无法访问问题
  • LeetCode.02.04.分割链表
  • Python深度学习基础——卷积神经网络(CNN)(PyTorch)
  • 第二章:Docker及Kubernetes基础 重难点详解_《再也不踩坑的kubernetes实战指南》
  • Simulink中Signal Builder在新版中找不到怎么办
  • leetcode12.整数转罗马数字
  • 从入门到进阶:React 图片轮播 Carousel 的奇妙世界!
  • linux查询inode使用率
  • Spring MVC 视图解析器(JSP、Thymeleaf、Freemarker、 JSON/HTML、Bean)详解
  • XML语法指南——从入门到精通
  • C#里使用WPF的MaterialDesignThemes
  • 回归预测 | Matlab实现RIME-CNN-GRU-Attention霜冰优化卷积门控循环单元注意力机制多变量回归预测
  • UM621系列模块安装校准及注意事项
  • 逍遥模拟器ARM过检测技术全解析
  • 使用mybatisplus-join自带的分页方法进行分页查询,不依靠pagehelper
  • 表 vs 物化视图:核心区别与选型指南
  • Asynchronous Advantage Actor-Critic(A3C)
  • 网站怎做/十大营销手段
  • 佛山做pc端网站/网络平台推广是干什么
  • python做网站怎么样/免费b站推广入口2023
  • 怎样制作自己网站/网络口碑营销案例
  • espcms易思企业网站/互联网推广员是做什么
  • 营销型网站建设必备功能/公司网站如何seo