树莓派开发(三)

裸机串口调试

Posted by 婷 on October 16, 2024 本文总阅读量

简介

裸机串口代码调试记录

代码

代码链接

https://github.com/copyright1999/raspi4b-project/tree/b0aabc649e0c5cd6c8ec4a42edd0d295af89231e/baremental

过程

主要的过程分为如下几个步骤

  • 链接脚本

  • 入口代码

  • 管脚复用

  • 串口驱动

  • Makefile

链接脚本

  • 确定好起始地址
  • 按照textdatabss的位置来分布(暂时还不考虑堆的事情)
  • 起点text

  • 字节对齐

image-20241016223956134

入口代码

主要抓住这几个点

  • 声明为text.boot段(因为前面的脚本指定了第一个text段就是text.boot段)

  • 设置所有寄存器初值为0
  • bss
  • 设置栈指针
  • 跳转C入口

尤其是设置栈指针,下面就是碰到一个栈问题,就是这里没设置,导入跳转C函数而出现异常

管脚复用

从树莓派的BCM2711 ARM Peripherals文档中可知,树莓派的UART1mini UART,其他都是PL011 UART。这里我们选择UART0来做这次的实验。

image-20241013105622973

我们找到UART0RXD,TXD的管脚

image-20241013110003102

image-20241013110020496

可以看到有GPIO14GPIO15,他的ALT0也就是第一种复用方式是作为UART0RXD,TXD

实物图对应如下

image-20241013110200439

至于怎么选择上下拉,可以看看这个文章

  • GPIO内部三种状态:上拉,下拉,不拉
  • 上下拉针对输入而言,输出一般选择不拉
  • 默认输入为0,最好配置下拉。默认输入为1,最好默认上拉
  • 上下拉是为了GPIO有个确定的输入状态,不然悬空很难保证初始默认电平

串口驱动

波特率的配置,从海思的手册上截取的

通过配置寄存器 UART_IBRD 和 UART_FBRD 可以设置 UART 工作的波特率,波特率计算公式为: 当前波特率=UART 参考时钟频率(100MHz、50MHz、24MHz 或 3MHZ)/(16 x 分频系数) 分频系数有整数和小数两部分组成,分别对应寄存器 UART_IBRD 和 UART_FBRD。

例如:UART 参考时钟频率为 24MHz,如果配置 UART_IBRD 为 0x1E,UART_FBRD 为 0x00,按照波特率计算公式,则当前的波特率为 24/(16 x 30)=0.05Mbit/s。 UART 波特率配置的典型值为:9,600bit/s、14,400bit/s、19,200bit/s、38,400bit/s、 57,600bit/s、76,800bit/s、115,200bit/s、230,400bit/s、460,800bit/s。

分频系数值的计算以及分频系数寄存器的配置举例如下: 如果要求波特率为 230,400bit/s,并且 UART 参考时钟频率为 24MHz,那么分频系数 为(24 x 106)/(16 x 230400)= 6.5104,因此 IBRD(整数部分)为 6,FBRD(小 数部分)为 0.5104。

计算 6bitUART_FBRD 寄存器中的数值:根据 m=integer(FBRDx2 n+0.5) (n=UART_FBRD 寄存器的宽度),计算出 m=integer(0.5104x2 6+0.5)=33,在 UART_IBRD 寄存器中配置 0x0006,UART_FBRD 寄存器中配置 0x21。 当分频系数小数部分配置成 33 时,波特率除数的实际数值为 6+33/2 6=6.5156,产生 的波特率为(24 x 106)/(16 x 6.5156)=230216.7107,误差率为(230216.7107– 230400)/230400x100=-0.07956%。 使用 6bitUART_FBRD 寄存器最大的误差率为 1/2 6 x 100=1.56%,当 m=1 时会出现, 误差率累计超过 64 个时钟周期。

等有空再来研究下吧

Makefile

这个写的比较简单,主要是注意要链接上我们的链接脚本就行

$(CROSS_COMPILE)ld -nostdlib start.o $(OBJS) -T link.ld -o baremetal.elf

调试遇到的问题

回车换行的问题

首先要知道两个定义

  • 回车符:\r,0x0D,使光标移到行首
  • 换行符:\n,0x0A,使光标下移一格

一开始无法正常换行,后来代码里面参考了别人的方法,只要是发送\r就都换成\n,只要是接收到\r就都换成\n。貌似PL011这个IP要这么操作,看DW的好像不用。

ubootPL011的代码看,确实也是这么处理的。

image-20241013114304173

栈问题

调试的时候,一开始代码没有初始化相关的寄存器,包括SP寄存器,导致在图中红框的汇编出,进入main函数,压栈的时候出错了,然后PC又跑飞了。

#将x29 x30存到 sp-16 所指向的内存地址上,x29在低地址,同时sp = sp -16
stp x29,x30,[sp,#-16]!

image-20241001191442230

那要怎么初始化SP寄存器呢,这样子操作就行了

image-20241013120614201

其他关于栈指针的汇编举例

ldr x0, [x1]                ; 从 x1 指向的地址中区一个 64 为数据存入 x0
ldp x1, x2, [x10, #10]      ; 从 x10+10 指向的地址中取 2 个 64 位的数,分别存入 x1, x2 低地址的存入 x1
str x5, [sp, #24]           ; 将 x5 内容存入 sp+24 所指向的内存地址上
stp x29, x30, [sp, #-16]!   ; 将 x29, x30 存到 sp-16 所指向的内存地址上,x29 在低地址,同时 sp = sp - 16 (感叹号的意思)
ldp x29, x30, [sp], #16     ; 从 sp 指向的内存地址中区两个 64 位数据存入 x29, x30 中,并且 sp += 16

地址映射的原因

反正我也不知道这个树莓派映射的道理,手册中的地址都是不对的,这个文档中提到外设的起始地址是0xFE000000,反正我也不打算去纠结这个事了,外设手册中UART的基地址是0x7e200000,实际代码用的是0xFE200000

image-20241014232516852

也可以看linux下的io memory分布

image-20241008232249935

参考链接

参考代码

文档链接

下一步代办

  • 封装代码,比如设置波特,设置停止位数据位等接
  • DMA模式,中断模式,inner loopback模式(若支持),outer loopback模式(若支持)
  • 流控研究
  • 示波器抓波形