树莓派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 汇编实现串口打印的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程