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.c
、mian.c
和led.h
分别放入src
和inc
文件夹中
三、修改主函数
修改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