一、构建程序目录
新建src文件夹,用来存放c源代码
新建inc文件夹,用来存放头文件
新建build文件夹,用来存放编译输出文件
二、编写源文件
在src文件夹下新建uart.c文件如下所示
- #include "uart.h"
- typedef unsigned int uint32_t;
- typedef struct
- {
- volatile uint32_t SR; /*!< USART Status register, Address offset: 0x00 */
- volatile uint32_t DR; /*!< USART Data register, Address offset: 0x04 */
- volatile uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */
- volatile uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */
- volatile uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */
- volatile uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */
- volatile uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */
- } USART_TypeDef;
- void uart_init(void)
- {
- USART_TypeDef *usart1 = (USART_TypeDef *)0x40013800;
- volatile unsigned int *pReg;
- /* 使能GPIOA/USART1模块 */
- /* RCC_APB2ENR */
- pReg = (volatile unsigned int *)(0x40021000 + 0x18);
- *pReg |= (1<<2) | (1<<14);
-
- /* 配置引脚功能: PA9(USART1_TX), PA10(USART1_RX)
- * GPIOA_CRH = 0x40010800 + 0x04
- */
- pReg = (volatile unsigned int *)(0x40010800 + 0x04);
-
- /* PA9(USART1_TX) */
- *pReg &= ~((3<<4) | (3<<6));
- *pReg |= (1<<4) | (2<<6); /* Output mode, max speed 10 MHz; Alternate function output Push-pull */
- /* PA10(USART1_RX) */
- *pReg &= ~((3<<8) | (3<<10));
- *pReg |= (0<<8) | (1<<10); /* Input mode (reset state); Floating input (reset state) */
-
- /* 设置波特率
- * 115200 = 8000000/16/USARTDIV
- * USARTDIV = 4.34
- * DIV_Mantissa = 4
- * DIV_Fraction / 16 = 0.34
- * DIV_Fraction = 16*0.34 = 5
- * 真实波特率:
- * DIV_Fraction / 16 = 5/16=0.3125
- * USARTDIV = DIV_Mantissa + DIV_Fraction / 16 = 4.3125
- * baudrate = 8000000/16/4.3125 = 115942
- */
- #define DIV_Mantissa 4
- #define DIV_Fraction 5
- usart1->BRR = (DIV_Mantissa<<4) | (DIV_Fraction);
-
- /* 设置数据格式: 8n1 */
- usart1->CR1 = (1<<13) | (0<<12) | (0<<10) | (1<<3) | (1<<2);
- usart1->CR2 &= ~(3<<12);
-
- /* 使能USART1 */
- }
-
- int getchar(void)
- {
- USART_TypeDef *usart1 = (USART_TypeDef *)0x40013800;
- while ((usart1->SR & (1<<5)) == 0);
- return usart1->DR;
- }
- int putchar(char c)
- {
- USART_TypeDef *usart1 = (USART_TypeDef *)0x40013800;
- while ((usart1->SR & (1<<7)) == 0);
- usart1->DR = c;
-
- return c;
- }
复制代码
在inc文件夹下新建uart.h内容如下
- #ifndef _UART_H
- #define _UART_H
- void uart_init(void);
- int getchar(void);
- int putchar(char c);
- #endif
复制代码
在src文件夹下新建led.c文件如下所示
- 在这里插入代码片#include "led.h"
- int delay(int ndelay)
- {
- volatile int n = ndelay;
- while(n--);
- return 0;
- }
- void led_init(void)
- {
- unsigned int *pReg;
-
- /* 1、使能GPIOB */
- pReg = (unsigned int *)(0x40021000 + 0x18);
- *pReg |= (1<<3);
-
- /* 2、设置GPIOB5为输出引脚 */
- pReg = (unsigned int *)(0x40010C00 + 0x00);
- *pReg |= (1<<20);
- pReg = (unsigned int *)(0x40010C00 + 0x0C);
- *pReg &= ~(1<<5);
- }
- void led_on(void)
- {
- unsigned int *pReg = (unsigned int *)(0x40010C00 + 0x0C);
-
- /* 设置GPIOB5输出0 */
- *pReg &= ~(1<<5);
- }
- void led_off(void)
- {
- unsigned int *pReg = (unsigned int *)(0x40010C00 + 0x0C);
-
- /* 设置GPIOB5输出1 */
- *pReg |= (1<<5);
- }
复制代码
在inc文件夹下新建led.h内容如下
- #ifndef __LED_H
- #define __LED_H
- int delay(int ndelay);
- void led_init(void);
- void led_on(void);
- void led_off(void);
- #endif
复制代码
三、修改主函数
将了原来的led.c修改为main.c如下所示
- #include "uart.h"
- #include "led.h"
- int main(void)
- {
- uart_init();
- led_init();
- putchar('s');
- putchar('t');
- putchar('m');
- putchar('3');
- putchar('2');
- putchar('f');
- putchar('1');
- putchar('0');
- putchar('3');
- putchar('\r');
- putchar('\n');
- while(1)
- {
- led_on();
- delay(1000000);
- led_off();
- delay(1000000);
- }
- }
复制代码
四、编写汇编程序
汇编程序也做相应修改
- .syntax unified /* 指明当前汇编文件的指令是ARM和THUMB通用格式 */
- .cpu cortex-m3 /* 指明cpu核为cortex-m3 */
- .fpu softvfp /* 软浮点 */
- .thumb /* thumb指令 */
- .global _start /* .global表示Reset_Handler是一个全局符号 */
- .word 0x00000000 /* 当前地址写入一个字(32bit)数据,值为0x00000000,实际上应为栈顶地址 */
- .word _start+1 /* 当前地址写入一个字(32bit)数据, 值为_reset标号代表的地址+1,即程序入口地址*/
- _start: /* 标签_start,汇编程序的默认入口是_start */
- /* 1、设置栈 */
- LDR SP, =(0x20000000+0x400)
- /* 2、跳转到led函数 */
- BL led
- /* 3、原地循环 */
- B .
复制代码
五、编写Makefile
编写Makefile如下所示,注意OBJECTS为我们所要链接的所有文件,链接时默认是按照其排列顺序来连接的,所以我们要将start.o文件放在最前面,如下所示;另外vpath用来指定该依赖文件的查找路径,还有关于-fno-builtin参数,表示不使用C语言的内建函数,因为我们编写的putchar()函数是C语言的内建函数,使用了-fno-builtin参数我们的函数名就可以和内建函数同名了。
- TARGET = uart
- C_SOURCES = src/main.c \
- src/uart.c \
- src/led.c
- C_INCLUDES = -Iinc
- ASM_SOURCES = start.s
- CPU = -mcpu=cortex-m3
- MCU = $(CPU) -mthumb
- CFLAGS = -c -g $(MCU) -Wall $(C_INCLUDES) -fno-builtin
- PREFIX = arm-none-eabi-
- CC = $(PREFIX)gcc
- AS = $(PREFIX)gcc -x assembler-with-cpp
- LD=$(PREFIX)ld
- CP = $(PREFIX)objcopy
- SZ = $(PREFIX)size
- OBJDUMP=$(PREFIX)objdump
- HEX = $(CP) -O ihex
- BIN = $(CP) -O binary -S
- 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 : $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
- $(SZ) [ DISCUZ_CODE_6 ]lt;
- $(BUILD_DIR)/%.o: %.c | $(BUILD_DIR)
- $(CC) $(CFLAGS) [ DISCUZ_CODE_6 ]lt; -o $@ -MMD -MP -MF"$(@:%.o=%.d)"
- $(BUILD_DIR)/%.o: %.s | $(BUILD_DIR)
- $(AS) $(CFLAGS) [ DISCUZ_CODE_6 ]lt; -o $@
- $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) | $(BUILD_DIR)
- $(LD) -g $(OBJECTS) -Ttext 0X8000000 -o $@
- $(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
- $(HEX) [ DISCUZ_CODE_6 ]lt; $@
-
- $(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
- $(BIN) [ DISCUZ_CODE_6 ]lt; $@
- $(BUILD_DIR)/.dis: %.elf
- $(OBJDUMP) -D -m cortex-m3 [ DISCUZ_CODE_6 ]lt; > $@
- $(BUILD_DIR):
- mkdir $@
- -include $(wildcard $(BUILD_DIR)/*.d)
- clean:
- rm -rf $(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函数如下所示
编译烧录运行可以看到其串口打印如下
|