树莓派4B 汇编实现串口打印
2021/7/22 9:36:44
本文主要是介绍树莓派4B 汇编实现串口打印,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
GPIO寄存器
在BCM2711中,共有58个GPIO管脚,被分成了3个banks,Bank0包含的GPIO从0到27,bank1包含的GPIO从28-45,bank2包含的GPIO从46到57。其中GPIO寄存器有GPFSELn、GPSETn、GPCLRn等,下面具体描述这些寄存器的作用:
-
寄存器GPFSEL0-GPFSEL5 表示功能寄存器,指定管脚为输入、输出等,每3位决定一个管脚:
-
000 = GPIO Pin 9 is an input
-
001 = GPIO Pin 9 is an output
-
100 = GPIO Pin 9 takes alternate function 0
-
101 = GPIO Pin 9 takes alternate function 1
-
110 = GPIO Pin 9 takes alternate function 2
-
111 = GPIO Pin 9 takes alternate function 3
-
011 = GPIO Pin 9 takes alternate function 4
-
010 = GPIO Pin 9 takes alternate function 5
-
其中:(寄存器---地址--描述)
对于树莓派4B有两种地址模式:Full 35-bit Address Map和Legacy master address
The peripheral addresses specified in this document are legacy master addresses. Software
accessing peripherals using the DMA engines must use 32-bit legacy master addresses. The Main
peripherals are available from 0x7C00_0000 to 0x7FFF_FFFF. Behind the scenes, the VideoCore
transparently translates these addresses to the 35-bit 0x4_7nnn_nnnn addresses.
So a peripheral described in this document as being at legacy address 0x7Enn_nnnn is available in the
35-bit address space at 0x4_7Enn_nnnn, and visible to the ARM at 0x0_FEnn_nnnn if Low Peripheral
mode is enabled.
寄存器 | 地址 | 描述 |
GPFSEL0 | 0xEF200000 | 决定GPIO0-GPIO9的管脚功能 |
GPFSEL1 | 0xEF200004 | 决定GPIO10-GPIO19的管脚功能 |
GPFSEL2 | 0xEF200008 | 决定GPIO20-GPIO29的管脚功能 |
GPFSEL3 | 0xEF20000C | 决定GPIO30-GPIO39的管脚功能 |
GPFSEL4 | 0xEF200010 | 决定GPIO40-GPIO49的管脚功能 |
GPFSEL3 | 0xEF200014 | 决定GPIO50-GPIO57的管脚功能 |
-
寄存器GPSET0-GPSET1 ,设置为1,每一位决定一个管脚
-
0 = NO effect
-
1 = Set GPIO pin n
-
汇编实现串口打印:
-
确定外设设备的基地址
定义所需的寄存器:
#define BASE_ADDR 0xFE000000 #define _GPFSEL1 0x200004 #define GPFSEL1 (BASE_ADDR+_GPFSEL1) #define U_BASE (BASE_ADDR+0x00201000) #define U_CR_REG (U_BASE+0x30) #define U_IBRD_REG (U_BASE+0x24) #define U_FBRD_REG (U_BASE+0x28) #define U_LCRH_REG (U_BASE+0x2C) #define U_IMSC_REG (U_BASE+0x38) #define U_FR_REG (U_BASE+0x18) #define U_DATA_REG (U_BASE)
-
确定串口的GPIO
GPIO14和GPIO15
从上图可以看出,我们应该将GPFSEL1的[14:12]和[17:15]设置为b100
ldr x1, =GPFSEL1 ldr w3, [x1] mov w4, 4 //设置GPFSEL[12:14] 为b100 bfi w3, w4, 12, 3 //设置GPFSEL[15:17] 为b100 bfi w3, w4, 15, 3 str w3, [x1]
-
关闭串口
ldr x0, =U_CR_REG str wzr, [x0]
-
设置波特率
设置波特率需要设置IBRD_REG和FBRD_REG
计算公式:
BAUDDIV = (FUARTCLK/(16 * Baud rate))
其中FUARTCLK是在config.txt定义,值为48*10^6
BAUDDIV = 48*16^6 / (16*115200) = 26.0416666667
integer part = 26
fractional part = (int)((0.0416666667 *64) + 0.5) = 3
generated baud rate divisor = 26 + (3 / 64) = 26.046875
generated baud rate = (48 * 10^6) / (16 * 26.046875) = 115177
error = |(115177 - 115200) / 115200 * 100| = 0.02%
ldr x0, =U_IBRD_REG mov w1, 26 str w1, [x0] ldr x0, =U_FBRD_REG mov w1, 3 str w1, [x0]
-
使能 FIFOs和 8 bits 帧
/* enable FIFOs and 8 bits frames */ ldr x0, =U_LCRH_REG mov w2, #0x70 str w2, [x0]
-
屏蔽串口的中断
ldr x0, =U_IMSC_REG str wzr, [x0]
-
使能串口(接收和发送)
/* enable UART, receive and transmit */ ldr x1, =U_CR_REG mov w2, #0x301 //1 | (1<<8) | (1<<9) str w2, [x1]
初始化代码如下:
.align 2 .global _init_pl_uart _init_pl_uart: ldr x1, =GPFSEL1 ldr w0, [x1] mov w4, 4 //设置GPFSEL[12:14] 为b100 bfi w3, w4, 12, 3 //设置GPFSEL[15:17] 为b100 bfi w3, w4, 15, 3 str w3, [x1] /* delay */ mov x0, #150 1: sub x0, x0, #1 cmp x0, #0 bne 1b isb //disable uart ldr x0, =U_CR_REG str wzr, [x0] /* * baud divisor = UARTCLK / (16 * baud_rate) = 48 * 10^6 / (16 * 115200) = 26.0416666667 integer part = 26 fractional part = (int) ((0.0416666667 * 64) + 0.5) = 3 generated baud rate divisor = 26 + (3 / 64) = 26.046875 generated baud rate = (48 * 10^6) / (16 * 26.046875) = 115177 error = |(115177 - 115200) / 115200 * 100| = 0.02% */ ldr x0, =U_IBRD_REG mov w1, #26 str w1, [x0] ldr x0, =U_FBRD_REG mov w1, #3 str w1, [x0] /* enable FIFOs and 8 bits frames */ ldr x0, =U_LCRH_REG mov w2, #0x70 str w2, [x0] /* mask interupts */ ldr x0, =U_IMSC_REG str wzr, [x0] /* enable UART, receive and transmit */ ldr x1, =U_CR_REG mov w2, #0x301 //1 | (1<<8) | (1<<9) str w2, [x1] isb ret
-
实现打印一个字符
判断uart flag寄存器能否传输
然后向UART Data寄存器写入数据
.global put_char_uart put_char_uart: mov x7, x30 ldr x1, =U_FR_REG 1: ldr w2, [x1] and w2, w2, #0x20 cmp w2, #0x0 b.ne 1b ldr x1, =U_DATA_REG str w0, [x1] mov x30, x7 ret
打印字符串:
.global put_string_uart put_string_uart: mov x4, x0 //save lr mov x6, x30 1: ldrb w0, [x4] bl put_char_uart add x4, x4, 1 cmp w0, #0 b.ne 1b //restore lr mov x30, x6 ret
打印EL等级:
.section .rodata .align 3 .globl el_string1 el_string1: .string "Booting at EL" .section .text .global print_el print_el: mov x10, x30 /* print EL */ adrp x0, el_string1 add x0, x0, :lo12:el_string1 bl put_string_uart mrs x5, CurrentEL /* get the currentEL value */ lsr x2, x5, #2 mov x0, #48 add x0, x0, x2 bl put_char_uart /* print the new line tab */ mov x0, #10 bl put_char_uart mov x30, x10 ret
所有的代码如下:
#define BASE_ADDR 0xFE000000 #define _GPFSEL1 0x200004 #define GPFSEL1 (BASE_ADDR+_GPFSEL1) #define U_BASE (BASE_ADDR+0x00201000) #define U_CR_REG (U_BASE+0x30) #define U_IBRD_REG (U_BASE+0x24) #define U_FBRD_REG (U_BASE+0x28) #define U_LCRH_REG (U_BASE+0x2C) #define U_IMSC_REG (U_BASE+0x38) #define U_FR_REG (U_BASE+0x18) #define U_DATA_REG (U_BASE) .section .rodata .align 3 .globl el_string1 el_string1: .string "Booting at EL" .section .text .align 2 .global put_char_uart put_char_uart: mov x7, x30 ldr x1, =U_FR_REG 1: ldr w2, [x1] and w2, w2, #0x20 cmp w2, #0x0 b.ne 1b ldr x1, =U_DATA_REG str w0, [x1] mov x30, x7 ret .global put_string_uart put_string_uart: mov x4, x0 //save lr mov x6, x30 1: ldrb w0, [x4] bl put_char_uart add x4, x4, 1 cmp w0, #0 b.ne 1b //restore lr mov x30, x6 ret .global _init_pl_uart _init_pl_uart: ldr x1, =GPFSEL1 ldr w0, [x1] mov w4, 4 //设置GPFSEL[12:14] 为b100 bfi w3, w4, 12, 3 //设置GPFSEL[15:17] 为b100 bfi w3, w4, 15, 3 str w3, [x1] /* delay */ mov x0, #150 1: sub x0, x0, #1 cmp x0, #0 bne 1b isb //disable uart ldr x0, =U_CR_REG str wzr, [x0] /* * baud divisor = UARTCLK / (16 * baud_rate) = 48 * 10^6 / (16 * 115200) = 26.0416666667 integer part = 26 fractional part = (int) ((0.0416666667 * 64) + 0.5) = 3 generated baud rate divisor = 26 + (3 / 64) = 26.046875 generated baud rate = (48 * 10^6) / (16 * 26.046875) = 115177 error = |(115177 - 115200) / 115200 * 100| = 0.02% */ ldr x0, =U_IBRD_REG mov w1, #26 str w1, [x0] ldr x0, =U_FBRD_REG mov w1, #3 str w1, [x0] /* enable FIFOs and 8 bits frames */ ldr x0, =U_LCRH_REG mov w2, #0x70 str w2, [x0] /* mask interupts */ ldr x0, =U_IMSC_REG str wzr, [x0] /* enable UART, receive and transmit */ ldr x1, =U_CR_REG mov w2, #0x301 //1 | (1<<8) | (1<<9) str w2, [x1] isb ret .global print_el print_el: mov x10, x30 /* print EL */ adrp x0, el_string1 add x0, x0, :lo12:el_string1 bl put_string_uart mrs x5, CurrentEL /* get the currentEL value */ lsr x2, x5, #2 mov x0, #48 add x0, x0, x2 bl put_char_uart /* print the new line tab */ mov x0, #10 bl put_char_uart mov x30, x10 ret
这篇关于树莓派4B 汇编实现串口打印的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-14后台交互资料入门指南
- 2024-11-14如何轻松创建项目环境:新手入门教程
- 2024-11-14如何抽离公共代码:初级开发者指南
- 2024-11-14Python编程入门指南
- 2024-11-14Python编程入门:如何获取参数
- 2024-11-14JWT 用户校验:简单教程与实践
- 2024-11-14Pre-commit 自动化测试入门指南
- 2024-11-14Python编程基础
- 2024-11-14Server Action入门教程:轻松掌握服务器操作
- 2024-11-14Server Component入门教程:轻松搭建服务器组件