Raspberry Pi 4B bare metal program serial uart implementation

background

Recently, I am learning the implementation of simple bare-metal programs on Raspberry Pi. The first thing I use is to transplant my own uboot, and perform simple configuration on uboot, and load the bare-metal programs compiled by myself and saved on the remote host through tftp, and realize the bare-metal programs. The first function must be that the host computer reads and sends data to the Raspberry Pi development board through the serial port as a console.
Raspberry Pi has two types of 5 (should be 5) serial controllers. One type is miniuart, the other is common PL011 serial port controller, except for uart1 which is miniuart, they are all common serial ports of PL011. Most of the information found on the Internet uses the miniuart of the Raspberry Pi to realize the console function of the program, refer to part3 in https://github.com/isometimes/rpi4-osdev.
(The follow-up is my personal experience, for reference only)
But I I encountered the problem of outputting garbled characters during the implementation process (the MobaXterm program was used as the host’s serial port receiving and sending tool), the reason should be the problem of the baud rate setting (the originally configured baud rate is 115200, the host machine modified the wave After the bit rate reaches 57600, the output is no longer garbled but the output characters cannot correspond to the actual ones), try to modify the register configuration and modify config.txt but fail.
By consulting the relevant introductions on the Internet, I simply understand that miniuart does not have its own clock, but uses the CPU clock, and there are also some other functional castrations. The clock frequency and baud rate are strongly correlated, so I think the reason for the error should be That's why. So I abandoned the miniuart on github as the console and configured it as an ordinary pl011 serial port controller. For the PL011 serial port controller, please refer to: ARM PrimeCell UART(PL011)

step

1. Connect the serial tool to the computer

I personally use the CH340 USB to TTL module connection.

  • When connecting the windows host,
    you need to install the corresponding serial port driver. After the USB port is plugged into the computer, you can see the corresponding serial port device in the device manager as shown in the figure below. (The picture is
    The Windows host recognizes the serial port device
    from github, hacked and deleted.)
    The real situation may be different. (Currently there is no experimental environment that needs to update the actual picture later).
    Open the serial port option of MobaXterm, select the corresponding serial device, that is, the COM number, and set the baud rate to open the serial device on the host side.
  • When connecting to a Linux host,
    there is no need to install any drivers. When a serial device is inserted, a ttyUSBx node will be generated in the /dev/ directory (the value of x is subject to the actual situation).
    Install the minicom serial port tool in the Linux host, use minicom -s the command to set the baud rate of the serial port, and then run sudo minicom -D /dev/ttyUSBx to open the corresponding serial port device

  • No experimental environment when connected to a Mac . . . (what a sad reason)

2. Connect Raspberry Pi

CH340 can have 4 pins, and there is a mark on the back of the small board.
GND: Ground, usually connected to a black Dupont line
Rx: Receive data from the Raspberry Pi, usually a white Dupont line, which can be compared to the mouth
Tx: Write data to the Raspberry Pi , generally a green Dupont line, which can be compared to the ear
Vcc: power supply, usually a red Dupont line,
and the value of the Vcc voltage can be selected, generally 3v3 and 5v, choose one through the jumper cap, it is not clear what the impact is, welcome readers The comment added, I chose 3v3. The color of the Dupont thread has no effect, it just conforms to the general usage habits.
Special attention should be paid to the fact that in the application, the Rx of the CH340 needs to be connected to the Tx of the board, and the Tx of the CH340 is connected to the Rx of the board. It is similar to the relationship between the mouth and the ear. The mouth needs to speak to the ear. It is easy to understand the logic of finding that your mouth speaks to the opposite mouth, and your ears listen to the sound of the opposite ear. In addition, GND needs to be connected to realize the common ground between the CH340 serial board and the Raspberry Pi, so as to avoid garbled data received or sent due to the difference in level. Vcc generally can not be connected.
On the Raspberry Pi board, we choose 14 and 15 as the serial port pins we configure, as shown in the figure below.
Raspberry Pi pin diagram
According to the above introduction, the schematic diagram after connection is as follows.
Schematic Diagram of Raspberry Pi Serial Cable Connection
In fact, the connection method is the same as that on github. Pins 14 and 15 of the Raspberry Pi can be reused as UART0 and UART1. UART0 is a common PL011 serial port controller, and UART1 is a miniUART.

3. Serial configuration

Regarding the link files in the code, the initialization program, etc., this article will not go into details. You can refer to the code warehouse 02_helloworld , and focus on how to configure the serial port. For the configuration of the serial port, please refer to the official document bcm2711 peripherals .
First of all, the complete serial port bare-metal program probably includes the following steps:

uart_init();
uart_send("Hello world!\n");
while(1)
{
    
    
        uart_send_char(uart_recv());
}

Among them, uart_init is the uart initialization function, uart_send(char *s) function is the function of sending character string, uart_send_char(char c) function is the function of sending character. uart_recv() is a function for receiving characters, which will be explained separately next.

  • uart_init()
    initializes the serial port, mainly completes the serial port pin configuration and serial port controller register configuration, the content is as follows.
void uart_init(void)
{
    
    
    unsigned int selector;

    selector = read_reg32(GPFSEL1); // get gpio control register value
    selector &= ~(7<<12);                   // clean gpio14
	selector |= 4<<12;                      // set alt0 for gpio14
	selector &= ~(7<<15);                   // clean gpio15
	selector |= 4<<15;                      // set alt0 for gpio15
    write_reg32(GPFSEL1, selector);
    
    write_reg32(GPPUD,0);
    delay(150);
    write_reg32(GPPUDCLK0,(1<<14)|(1<<15));
    delay(150);
    write_reg32(GPPUDCLK0,0);

    write_reg32(UART0_CR, 0); //disable uart0
    write_reg32(UART0_LCRH, 0x60); // set to character mode, 8 bits word len and disable Parity
    write_reg32(UART0_IBRD, 0x1a);
    write_reg32(UART0_FBRD, 0x3); // set burdrate to 115200
    write_reg32(UART0_DMACR, 0x00); //disable dma
    write_reg32(UART0_ITCR, 0x0); 
    write_reg32(UART0_ITOP, 0x0); 
    write_reg32(UART0_TDR, 0x0); 
    write_reg32(UART0_CR, 0x301); //enable uart0
}

Description: write_reg32 is a function for writing registers, and the corresponding read_reg32 is a function for reading registers. The delay() function is not in ms, but executes n times of empty instructions. The macro definition of the register address used in it is defined in the source docker file. Different from the address introduced in the official document, the address of PERIPHERAL_BASE in the document is 0x7e000000, and we use 0xfe000000. The main reason is that we use the low_memery mode. The first is to configure two pins. The 40 pins
on the Raspberry Pi have default The 6 functions of Alt0, Alt1...Alt5. As shown in the figure below, you only need to configure specific pins to specific functions to complete specific functions.
Raspberry Pi Pin Equations
As shown in the figure above, we need to select ALT0 for pins 14 and 15 (miniuart selects ALT5). The configuration method can be operated according to the instructions in the official document to configure the GPFSEL1 register. The description of the register in the document is as follows.
Description of pin function selection
As shown above, we only need to configure bits 17-12, and the corresponding code is as follows.

    selector = read_reg32(GPFSEL1); // get gpio control register value
    selector &= ~(7<<12);                   // clean gpio14
	selector |= 4<<12;                      // set alt0 for gpio14
	selector &= ~(7<<15);                   // clean gpio15
	selector |= 4<<15;                      // set alt0 for gpio15
    write_reg32(GPFSEL1, selector);

After configuring the function, we need to configure gpio to have no initial state, because the Raspberry Pi can be pulled high or low by default, but our pins can be used from the beginning, so there is no need to configure the initial state, GPPUDCLK0 and GPPUD need to be configured register. It should be pointed out that each configuration of these two registers will run 150 empty instructions (required by the document). code show as below:

    write_reg32(GPPUD,0);
    delay(150);
    write_reg32(GPPUDCLK0,(1<<14)|(1<<15));
    delay(150);
    write_reg32(GPPUDCLK0,0);

Finally, configure the controller of UART0, first turn off the serial port controller, and then configure it in character mode, which does not apply to fifo mode. Then set the baud rate as shown in the code. Turn off DMA and finally turn on uart0. The code is as follows. The calculation formula of the baud rate and the configuration method of the register are described in detail in the document, so I won’t go into details.

    write_reg32(UART0_CR, 0); //disable uart0
    write_reg32(UART0_LCRH, 0x60); // set to character mode, 8 bits word len and disable Parity
    write_reg32(UART0_IBRD, 0x1a);
    write_reg32(UART0_FBRD, 0x3); // set burdrate to 115200
    write_reg32(UART0_DMACR, 0x00); //disable dma
    write_reg32(UART0_ITCR, 0x0); 
    write_reg32(UART0_ITOP, 0x0); 
    write_reg32(UART0_TDR, 0x0); 
    write_reg32(UART0_CR, 0x301); //enable uart0
  • The source code of uart_send_char(char c)
    is as follows:
void uart_send_char(char c)
{
    
    
    if(c == '\n')   
        uart_send_char('\r');
    while(1){
    
    
        if(read_reg32(UART0_FR)&0x80 || !(read_reg32(UART0_FR)&0x20))
            break;
    }
    write_reg32(UART0_DR, c);
}

The '\r' character needs to be sent before sending the newline character '\n', which is related to the escape character of the serial port and has not been studied in depth.
It is necessary to wait for the frame status register of the serial port, read and read whether it is writable in a loop, if it is writable, write characters to the data register, otherwise, wait in a loop. (interrupt control not yet used)

  • uart_send(char *s)
void uart_send(char* str)
{
    
    
    while(*str!='\0'){
    
    
        uart_send_char(*str);
        str++;
    }
}

Loop call uart_send_char(char c) function to write characters.

  • uart_recv()
char uart_recv(void)
{
    
    
    while (1){
    
    
        if(read_reg32(UART0_FR)&0x40)
            break;
    }
    return(read_reg32(UART0_DR)&0xff);
}

Contrary to writing, reading is to read whether the frame status register data is ready, if ready, read

verify

After connecting the serial port Uboot starts, press any key to enter the command line (for Uboot configuration and control from the serial port, please refer to [Raspberry Pi Study Notes] Run uboot on Raspberry Pi 4B and start the linux kernel from the network (above) and [Raspberry Pi Study Notes] Run uboot on Raspberry Pi 4B and start the linux kernel from the network (below) document), respectively type tftp 0x80000 kernel8.imgand go 0x80000(in fact, it can also be set bootcmd= tftp 0x80000 kernel8.img; go 0x80000so that uboot will automatically load the bare-metal program and execute it every time it starts) to execute the bare-metal program, you can See the output in the serial port.

The code in this tutorial can be obtained at the following address:

Be sure to note that the source code needs to modify the cross compiler path first

Raspberry Pi 4B bare-metal serial program source code (combined with uboot)

Welcome to criticize and correct

Guess you like

Origin blog.csdn.net/weixin_43328157/article/details/130218995