赞
踩
我们在调试的过程中,都比较喜欢直观的数据,这时候我们可以使用芯片的串口功能,把数据打印到电脑上,这样就可以清晰的看到数据的变化。
下面呢,就到串口打印的程序啦。
首先,先分享一下库函数版本的代码。
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
}
}
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
}
usart.h文件。
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
void Usart_Init(uint32_t bound);
#endif
当我们把需要的代码写好并编译通过后,可以把程序烧录到单片机中。
注意:这里是使用小马哥的下载器和stm32最小系统板,连线如下图所示。
这时候,打开串口助手,按下单片机上的复位按键,即可以看到信息被打印了出来。
当然也可以用逻辑分析仪抓取下芯片的TX引脚发出的波形。
用逻辑分析仪自带的解析工具,对usart的单片机Usart1_Tx(PA9)引脚波形进行解析,可以对着ascii表找到十进制对应的字符即可。
RCC->APB2ENR |= (uint32_t)0x00000004;//打开GPIOA时钟
RCC->APB2ENR |= (uint32_t)0x00004000;//打开USART1时钟
打开GPIOA和USART1的时钟。
PA9(Usart1_Tx)设置为复用推挽输出,速度为50mhz。
GPIOA->CRH &= (uint32_t)0xFFFFFF0F;//使用前清零
GPIOA->CRH |= (uint32_t)0x000000B0;//配置PB9为复用推挽输出,最大速度50MHz
PA10(Usart1_Rx)设置为浮空输入。
GPIOA->CRH &= (uint32_t)0xFFFFF0FF;//使用前清零
GPIOA->CRH |= (uint32_t)0x00000400;//配置PA10为浮空输入
控制CR2寄存器的代码如下,对12、13位进行操作。
USART1->CR2 &= (uint16_t)0xCFFF; //使用前清零
USART1->CR2 |= (uint16_t)0x0000; //停止位设置为1位
控制CR2寄存器的代码如下,对2、3、9、10、12位进行操作。
USART1->CR1 &= (uint16_t)0xE9F3; //使用前清除 M, PCE, PS, TE and RE位
USART1->CR1 |= (uint16_t)0x000C; //设置一个起始位,8个数据位,无奇偶校验,使能发送,使能接收
下图是几个关键位的介绍
控制CR3寄存器的代码如下,对8、9位进行操作。
USART1->CR3 &= (uint16_t)0xFCFF; //使用前清除CTSE,RTSE位
USART1->CR3 |= (uint16_t)0x0000; //无硬件数据流控制
在分析官方的usart库的时候,遇到了一个函数,这里解释一下。
RCC_GetClocksFreq(&RCC_ClocksStatus);
这个函数主要的功能是得到重要时钟的频率。
通过keil5调试功能可以发现,这个函数主要得到5类频率。
SYSCLK、HCLK、PCLK1、PCLK2、ADCCLK的时钟频率。
下图是stm32单片机的使用的时钟树。自己理解的可能不到位,就不误导大家了。具体参考请看下面的博客。
参考博客:STM32F103时钟树详解
在官方给的例程中,这里有一些繁琐的计算操作。这里面其实也有一定的逻辑,并不是毫无意义的。
这里有个寄存器也需要重点关注。其中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;
第一句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寄存器写入值即可。
USART1->CR1 |= (uint16_t)0x2000;
#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
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。