16650串口初始化代码
static unsigned int uart16550_clock = 1843200; //串口时钟
#define UART_DEFAULT_BAUD 115200
void uart_init(void)
{
unsigned int divisor = uart16550_clock / (UART_DEFAULT_BAUD * 16);
/*关闭中断*/
writeb(0, UART_IER);
/*打开DLB字段,设置波特率分频*/
writeb(0x80, UART_LCR);
writeb((unsigned char)divisor, UART_DLL);
writeb((unsigned char)(divisor >> 8), UART_DLM);
/*设置串口数据格式*/
writeb(0x3, UART_LCR);
/*使能FIFO缓冲区,清空FIFO缓冲区,设置14字节阈值*/
writeb(0xc7, UART_FCR);
}
以上代码摘自《RISC-V 体系结构编程与实践》第一版,P39。
Deepseek思考内容
嗯,我现在需要理解这段UART初始化的代码。首先,用户定义了一个函数uart_init,里面做了一些UART的配置。让我仔细看一下每一部分的作用。
首先,divisor被计算为uart16550_clock除以(UART_DEFAULT_BAUD乘以16)。这里应该是在设置波特率的除数。因为UART通常使用除数来生成正确的波特率,而公式通常是除数=时钟频率/(波特率*16)。所以这一步应该是正确的。
接下来是关闭中断,用writeb(0, UART_IER)。UART_IER是中断使能寄存器,写入0的话就是禁止所有中断,这应该在初始化的时候避免中断干扰,对吧?所以这一步没问题。
然后,打开DLB字段,设置波特率分频。这里写入0x80到UART_LCR。LCR是线路控制寄存器,最高位(DLAB,即Divisor Latch Access Bit)是用来允许访问除数锁存器的。所以这时候设置DLAB为1,允许设置DLL和DLM的值。这一步是对的,因为需要先开启DLAB才能设置波特率的除数。
接着,写入divisor的低8位到UART_DLL,高8位到UART_DLM。这里要注意的是,divisor是一个unsigned int,可能超过8位,所以分两次写入。这里是否正确呢?假设uart16550_clock和UART_DEFAULT_BAUD的值是合适的,divisor的值不会太大,比如如果时钟是1.8432MHz,波特率是9600,那除数就是1.8432e6/(9600*16)=12,所以DLL是12,DLM是0。这时候没问题。但如果divisor的高8位超过的话,可能需要强制转换为unsigned char,防止高位被截断。不过代码里已经做了强制转换,所以应该没问题。
然后设置串口的数据格式,写入0x03到UART_LCR。这时候LCR的低两位是设置数据位长度为8位(因为0x3对应的是8位,因为每个位的意义是:位0和位1决定数据位数,00是5,01是6,10是7,11是8),其他位比如停止位,奇偶校验位等。0x03的话,应该是8位数据,1位停止位,无校验。所以这一步正确。
接下来是使能FIFO,清空FIFO,设置14字节阈值。写入0xc7到UART_FCR。FCR的各位含义是:位0是FIFO使能,位1和2是清空接收和发送FIFO,位6和7设置触发阈值。0xc7二进制是11000111。那这里可能有问题,因为FCR的位6和7是用于设置触发级别的,例如,对于16750,可能的触发值如14字节需要特定的位组合。比如,14字节对应的位可能是两位的组合。例如,00是1字节,01是4字节,10是8字节,11是14字节。所以位7和6设为11的话,就是14字节触发。所以这里写入0xC7的话,低两位是11,也就是使能FIFO(位0),并且位1和2是1,表示清除接收和发送FIFO。然后位7和6是11,设置触发级别为14字节。对吗?但FCR的各个位的布局需要确认。比如,FCR的结构是:Bit 0: Enable FIFOs;Bit 1: Clear接收FIFO;Bit 2: Clear发送FIFO;Bit 3: DMA模式选择;Bit 5-4: Reserved;Bit 7-6: 触发级别。所以,当写入0xC7的话,二进制是11000111。其中,Bit7-6是11,即3,对应触发级别14字节。Bit1和Bit2是1,即清除接收和发送FIFO。Bit0是1,使能FIFO。所以这样设置是正确的。
那有没有可能的问题呢?比如,在设置波特率分频的时候,是否应该先关闭FIFO?或者是否在设置FCR的时候有其他需要注意的地方?
另外,在设置LCR的时候,当写入0x80时,是否会影响其他位的设置?比如,在之后设置LCR为0x03的时候,是否会把DLAB位重新置0?是的,因为在设置完DLL和DLM之后,将LCR设置为0x03,此时DLAB位(最高位)是0,这样后续访问的寄存器就不是DLL和DLM了,而是其他寄存器,比如RBR/THR等。这个是正确的步骤。
综上,这段代码的流程是对的:关闭中断,设置DLAB为1,写入波特率除数,然后设置数据格式(同时关闭DLAB),然后配置FIFO。可能需要注意的地方是,在设置FIFO的时候,是否应该在其他步骤之后?比如,是否在设置波特率前需要禁用FIFO?不过根据UART的初始化流程,通常的步骤是先设置波特率,然后设置数据格式,然后配置FIFO。这里可能没问题。
另外,检查是否在设置FCR的时候,清空FIFO是否正确。因为当FCR的Bit1和Bit2被置1时,会清除FIFO。但这两个位是自动清零的,所以写入0xC7时,Bit1和Bit2会被置1,导致清除FIFO,并且其他位设置。硬件会自动将这些位清零吗?是的,这些位是自动清零的,所以写入后,FCR的值实际上是0xC7 & 0x0F,或者实际上的值可能由硬件处理,但代码中的操作是正确的。
有没有其他潜在的问题?比如,是否在初始化之后重新打开中断?但在这个函数里并没有开启中断,所以如果用户需要中断驱动的UART通信,可能需要在别处设置IER寄存器。但根据函数名uart_init,可能这个初始化函数仅完成基本的配置,中断的开启可能在别处处理。所以这应该不是问题。
所以,这段代码应该能正确初始化UART的波特率、数据格式、FIFO设置,并关闭中断。可能的改进点是否有没有?例如,是否应该等待FIFO清空完成?或者是否有其他的配置步骤?
可能的问题点:在某些情况下,设置波特率的时候可能需要先关闭FIFO。比如,在设置波特率时,是否应该确保FIFO被禁用?不过在这个代码中,FIFO是在设置波特率之后才被启用的,所以应该没有问题。因为设置波特率的时候,FCR可能还没配置,所以此时FIFO可能处于禁用状态。所以步骤是对的。
综上,这段代码是正确的,但需要确认UART的型号是否兼容这些设置。比如,假设使用的是16550兼容的UART,这些寄存器地址和位的定义是正确的。所以如果硬件符合16550的标准,这段代码应该是有效的。
Deepseek总结
这段代码用于初始化16550兼容的UART控制器,以下是逐部分的分析和总结:
- 波特率除数计算
unsigned int divisor = uart16550_clock / (UART_DEFAULT_BAUD * 16);
-
作用:计算波特率分频值。公式为 divisor = 时钟频率 / (波特率 × 16),符合UART标准波特率生成方法。
-
注意:需确保 uart16550_clock 和 UART_DEFAULT_BAUD 的值正确,避免除法截断错误。
- 关闭中断
writeb(0, UART_IER); // 关闭所有中断
- 作用:禁止UART所有中断(IER寄存器的所有位清零),防止初始化过程中意外中断触发。
- 设置波特率
writeb(0x80, UART_LCR); // 启用DLAB(Divisor Latch Access Bit)
writeb((unsigned char)divisor, UART_DLL); // 除数低字节
writeb((unsigned char)(divisor >> 8), UART_DLM); // 除数高字节
-
关键点:
-
设置LCR的最高位(DLAB)为1,允许访问波特率除数寄存器DLL/DLM。
-
将计算好的divisor写入DLL(低字节)和DLM(高字节),配置正确的波特率。
-
- 配置数据格式
writeb(0x3, UART_LCR); // 8位数据,1位停止位,无校验(DLAB已关闭)
-
解析:LCR设置为 0x03,对应:
-
数据位:8位(0b11)。
-
停止位:1位(LCR的位2为0)。
-
无校验(LCR的位3-5为0)。
-
DLAB位被清零,后续寄存器访问恢复正常模式。
-
- 配置FIFO
writeb(0xc7, UART_FCR); // 使能FIFO,清空缓冲区,设置14字节触发阈值
-
位解析(FCR寄存器):
-
Bit 0 (FIFO Enable):1(启用FIFO)。
-
Bit 1-2 (Clear FIFOs):1(清空接收和发送FIFO,操作后自动清零)。
-
Bit 7-6 (Trigger Level):0b11(14字节触发阈值,适用于16750等UART)。
-
潜在问题与改进建议
-
中断配置:初始化后中断仍被关闭,若需中断驱动通信,需额外设置IER寄存器。
-
FIFO触发阈值兼容性:14字节阈值可能仅适用于较新型号(如16750),确保硬件支持。
-
除数计算精度:确保divisor计算无整数截断,必要时添加四舍五入逻辑。
总结
代码正确实现了16550 UART的初始化流程,包括波特率设置、数据格式配置和FIFO使能。需确保硬件兼容性,并根据实际需求补充中断配置。
1万+

被折叠的 条评论
为什么被折叠?



