STM32裸机开发(5) — 在Keil-MDK下编写uart串口打印程序

STM32裸机开发(5) — 在Keil-MDK下编写uart串口打印程序

一、构建程序目录

新建src文件夹,用来存放c源代码
新建inc文件夹,用来存放头文件

二、编写源文件

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.cuart.c添加到工程中,并添加inc文件夹为头文件目录
在这里插入图片描述

三、修改主函数

将了原来的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);
    }
}

四、编写汇编程序

汇编程序start.s修改为如下所示

Stack_Size      EQU     0x00000400  				;定义堆栈大小为1024byte
				AREA    STACK, NOINIT, READWRITE, ALIGN=3  ;定义一个数据段,标记为STACK,即栈,不写入初始值初,对RAM来说,即初始化为08字节对齐
Stack_Mem		SPACE	Stack_Size    				;保留Stack_Size大小的栈空间
__initial_sp  										;标号,代表堆栈顶部地址,后面有用

                PRESERVE8							;指示编译器8字节对齐
                THUMB								;指示编译器以后的指令为THUMB指令								


; Vector Table Mapped to Address 0 at Reset
				AREA    RESET, CODE, READONLY		;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址
				EXPORT  __Vectors					;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用
					
__Vectors       DCD     __initial_sp				;当前地址写入一个字(32bit)数据,值应该为栈顶地址
                DCD     Reset_Handler              	;当前地址写入一个字(32bit)数据,值为Reset_Handler指向的地址值,即程序入口地址

				AREA    |.text|, CODE, READONLY		;定义代码段,标记为.text

; Reset handler	;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰
Reset_Handler   PROC								;过程的开始 
				EXPORT  Reset_Handler	[WEAK]		;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。
				IMPORT  main						;通知编译器要使用的标号在其他文件
				BL		main 						;跳转去执行main函数
				B		.							;原地跳转,即处于循环状态
				ENDP

                ALIGN 								;填充字节使地址对齐
                END									;整个汇编文件结束


五、运行

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

六、优化

接下来我们在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函数如下所示
在这里插入图片描述

扫描二维码关注公众号,回复: 12258173 查看本文章

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

七、附录

上一篇:STM32裸机开发(4) — 编写C语言点亮LED灯
下一篇:STM32裸机开发(6) — Keil-MDK下散列文件的分析
代码存放:https://gitee.com/william_william/stm32f103_noos/tree/master/keil-mdk/02_uart

猜你喜欢

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