STM32裸机开发(8) — 在gcc环境下编写uart串口打印程序

STM32裸机开发(8) — 在gcc环境下编写uart串口打印程序

一、构建程序目录

新建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) $<

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

$(BUILD_DIR)/%.o: %.s | $(BUILD_DIR)
	$(AS) $(CFLAGS)  $< -o $@

$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) | $(BUILD_DIR)
	$(LD) -g $(OBJECTS) -Ttext 0X8000000 -o $@

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(HEX) $< $@
	
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(BIN) $< $@

$(BUILD_DIR)/.dis: %.elf
	$(OBJDUMP) -D -m cortex-m3 $< > $@

$(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函数如下所示
在这里插入图片描述

编译烧录运行可以看到其串口打印如下
在这里插入图片描述

七、附录

上一篇:STM32裸机开发(7) — 复制data段和清除BSS段(ZI段)
下一篇:STM32裸机开发(9) — 使用链接脚本链接代码
代码存放:https://gitee.com/william_william/stm32f103_noos/tree/master/make-gcc/02_uart

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值