当前位置:   article > 正文

小兴教你做平衡小车-stm32程序开发(串口打印)_stm32小车

stm32小车

文章目录

  • 1 前言
  • 2 串口打印库函数版本
  • 3 串口打印寄存器版本
    • 3.1 配置时钟
    • 3.2 配置GPIO功能
    • 3.3 配置CR2寄存器
    • 3.4 配置CR1寄存器
    • 3.5 配置CR3寄存器
    • 3.6 库函数重要函数分析
    • 3.7 一些繁琐的计算
    • 3.8 USART使能
    • 3.9 寄存器代码汇总

1 前言

我们在调试的过程中,都比较喜欢直观的数据,这时候我们可以使用芯片的串口功能,把数据打印到电脑上,这样就可以清晰的看到数据的变化。

2 串口打印库函数版本

下面呢,就到串口打印的程序啦。
首先,先分享一下库函数版本的代码。

main.c文件。

#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
#include "stdio.h"

int main(void)
{
	RCC->APB2ENR |= (uint32_t)0x00000010;//打开GPIOC时钟
	GPIOC->CRH   &= (uint32_t)0xFF0FFFFF;//使用前清零
	GPIOC->CRH   |= (uint32_t)0x00300000;//配置PC13为推挽输出,最大速度50MHz
	Delay_Init();
	Usart_Init(115200);
	printf("hello world!\r\n");
	while(1)
	{
		GPIOC->BSRR  =  (uint32_t)0x00002000;//PC13引脚输出高电平
		Delay_ms(1800);                       //延时1800ms
		GPIOC->BRR  =   (uint16_t)0x2000;    //PC13引脚输出低电平
		Delay_ms(1800);                       //延时1800ms
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

usart.c文件。

#include "stm32f10x.h"
#include "usart.h"	
#include "stdio.h"

int fputc(int ch,FILE *f)
{
	USART_SendData(USART1,(uint8_t)ch);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待发送完成
	return ch;
}

void Usart_Init(uint32_t bound)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	USART_InitTypeDef  USART_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);//打开USART1和GPIOA的时钟
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;        //PA9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHZ
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_Init(GPIOA,&GPIO_InitStructure);  //初始化PA9引脚
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;        //PA10
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHZ
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入
	GPIO_Init(GPIOA,&GPIO_InitStructure);  //初始化PA10引脚
	
	USART_InitStructure.USART_BaudRate = bound;  //串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位
	USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位为1位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
	USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//发送接收模式
	USART_Init(USART1,&USART_InitStructure); //初始化USART1
	
	USART_Cmd(USART1,ENABLE);  //使能USART1
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

usart.h文件。

#ifndef __USART_H
#define __USART_H

#include "stm32f10x.h"

void Usart_Init(uint32_t bound);

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

当我们把需要的代码写好并编译通过后,可以把程序烧录到单片机中。
注意:这里是使用小马哥的下载器和stm32最小系统板,连线如下图所示。
在这里插入图片描述

这时候,打开串口助手,按下单片机上的复位按键,即可以看到信息被打印了出来。
在这里插入图片描述
当然也可以用逻辑分析仪抓取下芯片的TX引脚发出的波形。
在这里插入图片描述
用逻辑分析仪自带的解析工具,对usart的单片机Usart1_Tx(PA9)引脚波形进行解析,可以对着ascii表找到十进制对应的字符即可。
在这里插入图片描述

在这里插入图片描述

3 串口打印寄存器版本

3.1 配置时钟

	RCC->APB2ENR |= (uint32_t)0x00000004;//打开GPIOA时钟
	RCC->APB2ENR |= (uint32_t)0x00004000;//打开USART1时钟
  • 1
  • 2

打开GPIOA和USART1的时钟。
在这里插入图片描述

3.2 配置GPIO功能

PA9(Usart1_Tx)设置为复用推挽输出,速度为50mhz。

	GPIOA->CRH   &= (uint32_t)0xFFFFFF0F;//使用前清零
	GPIOA->CRH   |= (uint32_t)0x000000B0;//配置PB9为复用推挽输出,最大速度50MHz
  • 1
  • 2

PA10(Usart1_Rx)设置为浮空输入。

	GPIOA->CRH   &= (uint32_t)0xFFFFF0FF;//使用前清零
	GPIOA->CRH   |= (uint32_t)0x00000400;//配置PA10为浮空输入
  • 1
  • 2

在这里插入图片描述

3.3 配置CR2寄存器

控制CR2寄存器的代码如下,对12、13位进行操作。

	USART1->CR2  &= (uint16_t)0xCFFF; //使用前清零
	USART1->CR2  |= (uint16_t)0x0000; //停止位设置为1位
  • 1
  • 2

在这里插入图片描述

3.4 配置CR1寄存器

控制CR2寄存器的代码如下,对2、3、9、10、12位进行操作。

	USART1->CR1  &= (uint16_t)0xE9F3; //使用前清除 M, PCE, PS, TE and RE位
	USART1->CR1  |= (uint16_t)0x000C; //设置一个起始位,8个数据位,无奇偶校验,使能发送,使能接收
  • 1
  • 2

在这里插入图片描述
在这里插入图片描述
下图是几个关键位的介绍
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.5 配置CR3寄存器

控制CR3寄存器的代码如下,对8、9位进行操作。

	USART1->CR3  &= (uint16_t)0xFCFF; //使用前清除CTSE,RTSE位
	USART1->CR3 |=  (uint16_t)0x0000;    //无硬件数据流控制
  • 1
  • 2

在这里插入图片描述

3.6 库函数重要函数分析

在这里插入图片描述
在分析官方的usart库的时候,遇到了一个函数,这里解释一下。
RCC_GetClocksFreq(&RCC_ClocksStatus);这个函数主要的功能是得到重要时钟的频率。
在这里插入图片描述
通过keil5调试功能可以发现,这个函数主要得到5类频率。
SYSCLK、HCLK、PCLK1、PCLK2、ADCCLK的时钟频率。
在这里插入图片描述
下图是stm32单片机的使用的时钟树。自己理解的可能不到位,就不误导大家了。具体参考请看下面的博客。
参考博客:STM32F103时钟树详解
在这里插入图片描述

3.7 一些繁琐的计算

在官方给的例程中,这里有一些繁琐的计算操作。这里面其实也有一定的逻辑,并不是毫无意义的。
在这里插入图片描述
这里有个寄存器也需要重点关注。其中0-3位是USARTDIV的小数部分,4-15是USARTDIV的整数部分。
参考博客:STM32 USART_Init() 初始化函数中BRR寄存器设置解析
在这里插入图片描述

	uint32_t my_tmpreg = 0x00;
	uint32_t my_apbclock = 0x00;
	uint32_t my_integerdivider = 0x00;
	uint32_t my_fractionaldivider = 0x00;
	
	my_apbclock = 72000000; //因为目前使用的外设为Usart1,所以使用PCLK2(时钟频率为72000000)
	my_integerdivider = (25 * my_apbclock) / (4 * 115200);  //3906
	my_tmpreg = (my_integerdivider / 100) << 4; //624
	my_fractionaldivider = my_integerdivider - (100 * (my_tmpreg >> 4)); //6
	my_tmpreg |= ((((my_fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F);  //625
	USART1->BRR = (uint16_t)my_tmpreg;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

第一句my_apbclock = 72000000;因为目前使用的外设为Usart1,所以使用PCLK2(时钟频率为72000000)

第二句my_integerdivider = (25 * my_apbclock) / (4 * 115200);这句代码可以稍微变形一下。假设这里的/不是C语言整除的意思,可以带有小数。
my_integerdivider =(25 * my_apbclock) / (4 * 115200)= (100 * my_apbclock) / (16 * 115200)= 100 * (my_apbclock / (16 * 115200));
下图是数据手册里的解释,可以看出USARTDIV的计算方式。如果此时的波特率为115200,USARTDIV为39.0625。
如果此时的波特率为9600,USARTDIV为468.75。
在这里插入图片描述

再回到程序里面,
如果波特率为115200,my_integerdivider 为3906。
如果波特率为9600,my_integerdivider 为46875。

第三句my_tmpreg = (my_integerdivider / 100) << 4;这句话实现了把USARTDIV的整数部分位移置BRR寄存器的bit 15 : 4。

再回到程序里面,
如果波特率为115200,my_tmpreg 为39<<4=624。
如果波特率为9600,my_tmpreg 为468<<4=7488。

第四句my_fractionaldivider = my_integerdivider - (100 * (my_tmpreg >> 4));这句话是确定USARTDIV的小数部分。

再回到程序里面,
如果波特率为115200,my_fractionaldivider 为6。
如果波特率为9600,my_fractionaldivider 为75。

第五句my_tmpreg |= ((((my_fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F);
首先看(((my_fractionaldivider * 16) + 50) / 100),依然假设这里的/不是C语言整除的意思,可以带有小数。(((my_fractionaldivider * 16) + 50) / 100)=(my_fractionaldivider /100)*16+0.5

下面图片内容来自博客:STM32 USART_Init() 初始化函数中BRR寄存器设置解析

在这里插入图片描述
第六句USART1->BRR = (uint16_t)my_tmpreg;意思是向BRR寄存器写入值即可。

3.8 USART使能

	USART1->CR1 |= (uint16_t)0x2000;
  • 1

在这里插入图片描述

3.9 寄存器代码汇总

#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
#include "stdio.h"

int main(void)
{
	uint32_t my_tmpreg = 0x00;
	uint32_t my_apbclock = 0x00;
	uint32_t my_integerdivider = 0x00;
	uint32_t my_fractionaldivider = 0x00;
	
	RCC->APB2ENR |= (uint32_t)0x00000010;//打开GPIOC时钟
	GPIOC->CRH   &= (uint32_t)0xFF0FFFFF;//使用前清零
	GPIOC->CRH   |= (uint32_t)0x00300000;//配置PC13为推挽输出,最大速度50MHz
	Delay_Init();
	
	//串口寄存器版本
	//----------------串口初始化----------------//
	RCC->APB2ENR |= (uint32_t)0x00000004;//打开GPIOA时钟
	RCC->APB2ENR |= (uint32_t)0x00004000;//打开USART1时钟
	
	GPIOA->CRH   &= (uint32_t)0xFFFFFF0F;//使用前清零
	GPIOA->CRH   |= (uint32_t)0x000000B0;//配置PA9为复用推挽输出,最大速度50MHz
	GPIOA->CRH   &= (uint32_t)0xFFFFF0FF;//使用前清零
	GPIOA->CRH   |= (uint32_t)0x00000400;//配置PA10为浮空输入
	
	USART1->CR2  &= (uint16_t)0xCFFF; //使用前清除stop控制位
	USART1->CR2  |= (uint16_t)0x0000; //设置停止位为1位
	
	USART1->CR1  &= (uint16_t)0xE9F3; //使用前清除 M, PCE, PS, TE and RE位
	USART1->CR1  |= (uint16_t)0x000C; //设置一个起始位,8个数据位,无奇偶校验,使能发送,使能接收
	
	USART1->CR3  &= (uint16_t)0xFCFF; //使用前清除CTSE,RTSE位
	USART1->CR3 |=  (uint16_t)0x0000;    //无硬件数据流控制
	
	my_apbclock = 72000000; //因为目前使用的外设为Usart1,所以使用PCLK2(时钟频率为72000000)
	my_integerdivider = (25 * my_apbclock) / (4 * 115200);  //3906
	my_tmpreg = (my_integerdivider / 100) << 4; //624
	my_fractionaldivider = my_integerdivider - (100 * (my_tmpreg >> 4)); //6
	my_tmpreg |= ((((my_fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F);  //625
	USART1->BRR = (uint16_t)my_tmpreg;
	USART1->CR1 |= (uint16_t)0x2000;
	//----------------串口初始化----------------//
	
	//串口库函数版本
	//Usart_Init(115200);
	
	printf("hello world!\r\n");
	while(1)
	{
		GPIOC->BSRR  =  (uint32_t)0x00002000;//PC13引脚输出高电平
		Delay_ms(1800);                       //延时1800ms
		GPIOC->BRR  =   (uint16_t)0x2000;    //PC13引脚输出低电平
		Delay_ms(1800);                       //延时1800ms
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/614922
推荐阅读
相关标签
  

闽ICP备14008679号