IMX6ULL裸机学习(6)— 编写uart串口打印程序

IMX6ULL裸机学习(6)— 编写uart串口打印程序

一、构建程序目录

新建src文件夹,用来存放c源代码
新建inc文件夹,用来存放头文件
新建build文件夹,用来存放编译输出文件

二、编写源文件

src文件夹下新建uart.c文件如下所示

#include "uart.h"


/*根据IMX6ULL芯片手册<<55.15 UART Memory Map/Register Definition>>的3608页,定义UART的结构体,*/
typedef struct {
    
    
  volatile unsigned int  URXD;               /**< UART Receiver Register, offset: 0x0 	           串口接收寄存器,偏移地址0x0     */
  		   unsigned char RESERVED_0[60];		
  volatile unsigned int  UTXD;               /**< UART Transmitter Register, offset: 0x40          串口发送寄存器,偏移地址0x40*/
  		   unsigned char RESERVED_1[60];		
  volatile unsigned int  UCR1;               /**< UART Control Register 1, offset: 0x80 	       串口控制寄存器1,偏移地址0x80*/
  volatile unsigned int  UCR2;               /**< UART Control Register 2, offset: 0x84 	       串口控制寄存器2,偏移地址0x84*/
  volatile unsigned int  UCR3;               /**< UART Control Register 3, offset: 0x88            串口控制寄存器3,偏移地址0x88*/
  volatile unsigned int  UCR4;               /**< UART Control Register 4, offset: 0x8C            串口控制寄存器4,偏移地址0x8C*/
  volatile unsigned int  UFCR;               /**< UART FIFO Control Register, offset: 0x90         串口FIFO控制寄存器,偏移地址0x90*/
  volatile unsigned int  USR1;               /**< UART Status Register 1, offset: 0x94             串口状态寄存器1,偏移地址0x94*/
volatile unsigned int  USR2;               /**< UART Status Register 2, offset: 0x98             串口状态寄存器2,偏移地址0x98*/
  volatile unsigned int  UESC;               /**< UART Escape Character Register, offset: 0x9C     串口转义字符寄存器,偏移地址0x9C*/
  volatile unsigned int  UTIM;               /**< UART Escape Timer Register, offset: 0xA0         串口转义定时器寄存器 偏移地址0xA0*/
  volatile unsigned int  UBIR;               /**< UART BRM Incremental Register, offset: 0xA4      串口二进制倍率增加寄存器 偏移地址0xA4*/
  volatile unsigned int  UBMR;               /**< UART BRM Modulator Register, offset: 0xA8 	   串口二进制倍率调节寄存器 偏移地址0xA8*/
  volatile unsigned int  UBRC;               /**< UART Baud Rate Count Register, offset: 0xAC      串口波特率计数寄存器 偏移地址0xAC*/
  volatile unsigned int  ONEMS;              /**< UART One Millisecond Register, offset: 0xB0      串口一毫秒寄存器 偏移地址0xB0*/
  volatile unsigned int  UTS;                /**< UART Test Register, offset: 0xB4                 串口测试寄存器 偏移地址0xB4*/		
  volatile unsigned int  UMCR;               /**< UART RS-485 Mode Control Register, offset: 0xB8  串口485模式控制寄存器 偏移地址0xB8*/
} UART_Type;


void uart_init(void)
{
    
    
	volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA ;
	volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA	;
	volatile unsigned int *IOMUXC_UART1_RX_DATA_SELECT_INPUT ;
	volatile unsigned int *CCM_CSCDR1;
	volatile unsigned int *CCM_CCGR5;
	UART_Type *uart1 = (UART_Type *)0x02020000;

	IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA 	= (volatile unsigned int *)(0x20E0084);
	IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA 	= (volatile unsigned int *)(0x20E0088);
	IOMUXC_UART1_RX_DATA_SELECT_INPUT		= (volatile unsigned int *)(0x20E0624);
	CCM_CSCDR1 = (volatile unsigned int *)(0x020C4024);
	CCM_CCGR5 = (volatile unsigned int *)(0x020C407C);



	/* 设置UART的总时钟
	 * UART_CLK_ROOT = 80Mhz
	 */
	*CCM_CSCDR1 &= ~((1<<6) | (0x3f));

	/* 给UART1模块提供时钟
	 * uart1_clk_enable
	 */
	*CCM_CCGR5 |= (3<<24);

	/* 配置引脚功能 */
	*IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA &= ~0xf;
	*IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA &= ~0xf;

	/* IMX6ULL特殊的地方 */
	*IOMUXC_UART1_RX_DATA_SELECT_INPUT |= 3;

	/* 设置波特率 
	 * 115200 = 80M/(16*(UBMR+1)/(UBIR+1))
	 * UBIR = 15
	 * 115200 = 80M/(UBMR+1)
	 * UBMR = 80,000,000/115200 = 694 - 1 = 693
	 * 真正的baudrate = 80,000,000/694 = 115274
	 * 先设置UBIR, 后设置UBMR
	 */
	uart1->UFCR |= (5<<7);
	uart1->UBIR = 15;
	uart1->UBMR = 693;

	/* 设置数据格式 */
	uart1->UCR2 |= (1<<14) | (0<<8) | (0<<6) | (1<<5) | (1<<2) | (1<<1);

	/* IMX6ULL芯片要求必须设置 */
	uart1->UCR3 |= (1<<2);

	/* 使能UART */
	uart1->UCR1 |= (1<<0);	
}

int getchar(void)
{
    
    
	UART_Type *uart1 = (UART_Type *)0x02020000;

	while ((uart1->USR2 & (1<<0)) == 0);
	return uart1->URXD;
}

int	putchar(char c)
{
    
    
	UART_Type *uart1 = (UART_Type *)0x02020000;

	while ((uart1->USR2 & (1<<3)) == 0);
	uart1->UTXD = c;
	return c;
}

inc文件夹下新建uart.h内容如下

#ifndef _UART_H
#define _UART_H

void uart_init(void);
int getchar(void);
int	putchar(char c);

#endif

并将led.cmian.cled.h分别放入srcinc文件夹中

三、修改主函数

修改main.c如下所示

#include "uart.h"
#include "led.h"

int main(void)
{
    
    
    uart_init();
    led_init();
    putchar('i');
    putchar('m');
    putchar('x');
    putchar('6');
    putchar('u');
    putchar('l');
    putchar('l');
    putchar('\r');
    putchar('\n');

    while(1)
    {
    
    
        led_on();
        delay(1000000);
        led_off();
        delay(1000000);
    }
}

四、编写汇编程序

汇编程序和之前的一样


.text                               /* .text段保存代码,是只读和可执行的,后面的指令都属于.text段。 */
.global  _start                     /* .global表示_start是一个全局符号,会在链接器链接时用到 */

_start:                             /* 标签_start,汇编程序的默认入口是_start */
    /* 1、设置栈 */
    ldr sp, =(0x80000000+0x100000)  /* 设置栈顶地址 */
    /* 2、跳转到led函数 */
    bl main
    /* 3、原地循环 */
    b .

五、编写Makefile

编写Makefile如下所示,注意OBJECTS为我们所要链接的所有文件,链接时默认是按照其排列顺序来连接的,所以我们要将start.o文件放在最前面,如下所示;另外vpath用来指定该依赖文件的查找路径

TARGET = uart

C_SOURCES = src/main.c \
src/uart.c \
src/led.c

C_INCLUDES = -Iinc

ASM_SOURCES = start.s

CFLAGS = -c -g $(C_INCLUDES) -fno-builtin
PREFIX=arm-linux-gnueabihf-
CC=$(PREFIX)gcc
LD=$(PREFIX)ld
AR=$(PREFIX)ar
SZ=$(PREFIX)size
OBJCOPY=$(PREFIX)objcopy
OBJDUMP=$(PREFIX)objdump

BUILD_DIR = build

OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))

all : $(OBJECTS) | $(BUILD_DIR)
	$(LD) -g -Ttext 0x80100000 $^ -o $(BUILD_DIR)/$(TARGET).elf
	$(OBJCOPY) $(BUILD_DIR)/$(TARGET).elf -O binary -S $(BUILD_DIR)/$(TARGET).bin
	$(OBJDUMP) -D -m arm $(BUILD_DIR)/$(TARGET).elf > $(BUILD_DIR)/$(TARGET).dis
	$(SZ) $(BUILD_DIR)/$(TARGET).elf
	mkimage -T imximage -n ./tools/imximage.cfg.cfgtmp -e 0x80100000 -d $(BUILD_DIR)/$(TARGET).bin $(BUILD_DIR)/$(TARGET).imx
	dd if=/dev/zero of=$(BUILD_DIR)/1k.bin bs=1024 count=1
	cat $(BUILD_DIR)/1k.bin $(BUILD_DIR)/$(TARGET).imx > $(BUILD_DIR)/$(TARGET).img

$(BUILD_DIR)/%.o: %.c | $(BUILD_DIR)
	$(CC) -c $(CFLAGS) $< -o $@ -MMD -MP -MF"$(@:%.o=%.d)"

$(BUILD_DIR)/%.o: %.s | $(BUILD_DIR)
	$(CC) -c $(CFLAGS) $< -o $@ -MMD -MP -MF"$(@:%.o=%.d)"
	
$(BUILD_DIR):
	mkdir $@
		
-include $(wildcard $(BUILD_DIR)/*.d)

clean:
	-rm -fR $(BUILD_DIR)

然后make命令执行编译
在这里插入图片描述

然后烧录运行可以看到其打印的信息
在这里插入图片描述

六、优化

接下来我们在uart.c中添加如下两个函数用来打印字符串和16进制数

int putstring(const char *s)
{
    
    
	while (*s)
	{
    
    
		putchar(*s);
		s++;
	}
	return 0;
}

void puthex(unsigned int val)
{
    
    
	/* 0x76543210 */
	int i, j;

	//puts("0x");
	putchar('0');
	putchar('x');
	for (i = 7; i >= 0; i--)
	{
    
    
		j = (val >> (i*4)) & 0xf;
		if ((j >= 0) && (j <= 9))
			putchar('0' + j);
		else
			putchar('A' + j - 0xA);
	}	
}

修改main函数如下所示
在这里插入图片描述
编译烧录运行可以看到其串口打印如下
在这里插入图片描述

七、附录

上一篇:IMX6ULL裸机学习(5)— 编写C语言程序点亮LED
下一篇:IMX6ULL裸机学习(7)— 使用链接脚本链接代码
代码存放:https://gitee.com/william_william/imx6ull_noos/tree/master/02_uart

猜你喜欢

转载自blog.csdn.net/qq_38113006/article/details/112127440