?百问网f103开发板笔记
STM32F103有3个通用同步异步收发器(USART), 2个通用同步异步收发器( UART),USART和UART的主要区别在于, USART支持同步通信, 该模式有一根时钟线提供时钟。 ?
串口在嵌入式中经常使用,?常见的用途如下: 1. 作为调试口, 打印程序运行的状态信息; 2. 连接串口接口的模块(比如GPS模块),传输数据; 3. 通过电平转换芯片变为RS232/RS485电平,连接工控设备;
?可以把USART分成四部分: 1: USART引脚 TX:数据发送; ?RX:数据接收; SW_RX: 在单线和智能卡模式下接收数据, 属于内部引脚,没有具体外部引脚; RTS: 在硬件流控制时, 用于指示本设备准备好可接收数据, 低电平说明本设备可以接收数据; CTS:在硬件流控制时, 用于指示本设备准备好可发送数据, 低电平说明本设备可以发送数据; CK:在同步模式时,用于输出时钟;?
?2: 波特率发生器 通过设置USART_BRR寄存器的值,实现串口通信数据传输速率的设置。 由《参考手册》可知计算公式为:????????
其中“为波特率,为该外设USART的时钟频率,这里的fPCLKx(x=1、2)是给外设的时钟(PCLK1用于USART2、3、4、5,PCLK2用于USART1)?,USARTDIV”为USART_BRR寄存器的值。 假设所需波特率为115200,当前USART时钟为72MHz,则USARTDIV=72000000/(115200*16)=39.0625。 USART_BRR寄存器使用高12位[15:4]存放整数部分, 低4位[3:0]存放小数部分, 小数部分每一位对应1/24=0.0625。 因此,整数39对应16进制为0x27,左移4位为0x270,小数0.0625, 对应0x1, USART_BRR=0x271即可。 在利用寄存器配置USART的波特率的时候需要依据此公式计算USART_BRR的值,而在HAL库中无需计算, 只需传入所需波特率,自动写USART_BRR寄存器值,但是我们仍然要学习这个波特率的计算公式,也许的开发调试过程中会使用到。
前面计算波特率需要知道外设时钟的值,?USART1挂载APB1上, USART2/3 和USART4/5挂载APB2上。?APB1时钟最大为36MHz, APB2时钟最大为72MHz。 因此, 只有USART1的波特率计算中的能取最大系统时钟72MHz,而其它的USART/UART只能取36MHz。
?3: 发送器/接收器控制单元 通过向控制寄存器CR1、 CR2、 CR3和状态寄存器SR写入相应的位,可实现对USART数据的发送和接收控制。 其中CR1主要用于配置USART的数据位、校验位和中断使能, CR2用于配置USART的停止位和SCLK时钟控制, CR3用于CTS硬件流控制、 DMA多缓冲控制等。通过读取状态寄存器SR的值,可查询USART的状态。 4:数据收发寄存器单元 该部分主要由发送数据寄存器( TDR)、发送移位寄存器、接收数据寄存器( RDR)、接收移位寄存器组成。 发送移位寄存和接收移位寄存器,分别负责将发送数据并串转换和接收数据串并转换,从而实现数据在传输时,是一位一位的发送和接收
hal库手写实现
需要两个文件
?stm32f1xx_conf.h配置
?
?
初始化USART USART初始化包含两部分: 协议部分和硬件部分。 协议部分与硬件无关, 比如USART的波特率、奇偶校验、停止位等, 通过“ HAL_UART_Init()” 设置。 硬件部分指承载的硬件载体,比如串口所使用的发送、 接收引脚的复用,通过“ HAL_UART_MspInit()” 设置。 函数名中的Msp( MCU Specific Package, MCU特定软件包), 就是指MCU相关的硬件初始化。 ?
driver_usart.h(引脚宏定义)
/*********************
* 引脚宏定义
**********************/
#define USARTx USART1
#define USARTx_TX_PIN GPIO_PIN_9
#define USARTx_RX_PIN GPIO_PIN_10
#define USARTx_PORT GPIOA
#define USARTx_GPIO_CLK_EN() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USARTx_CLK_EN() __HAL_RCC_USART1_CLK_ENABLE()
#define USARTx_CLK_DIS() __HAL_RCC_USART1_CLK_DISABLE()
driver_usart.c
void HAL_UART_MspInit(UART_HandleTypeDef* husart)
{
// 定义 GPIO 结构体对象
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(husart->Instance==USARTx)
{
// 使能 USART1 的时钟
USARTx_CLK_EN();
// 使能 USART1 的输入输出引脚的时钟
USARTx_GPIO_CLK_EN();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = USARTx_TX_PIN; // 选择 USART1 的 TX 引脚
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 配置为复用推挽功能
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 引脚翻转速率快
HAL_GPIO_Init(USARTx_PORT, &GPIO_InitStruct); // 初始化 TX 引脚
GPIO_InitStruct.Pin = USARTx_RX_PIN; // 选择 RX 引脚
GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT; // 配置为输入
HAL_GPIO_Init(USARTx_PORT, &GPIO_InitStruct); // 初始化 RX 引脚
}
}
HAL_UART_Init()会调用“HAL_UART_MspInit()” , 从而实现对涉及的硬件初始化, 用户需要覆 写( HAL库提供函数名, 函数内容需要自己编写)该函数,完成使能串口时钟、初始化TX/RX的引脚、设置USART1的中断优先级且使能中断等。
driver_usart.c
/*
* 定义全局变量
*定义一个“ UART_HandleTypeDef”结构体(官方也称句柄)变量husart,用于保存串口参数设置
*/
UART_HandleTypeDef husart;//
void UsartInit(uint32_t baudrate)
{
husart.Instance = USARTx; // 选择 USART1
husart.Init.BaudRate = baudrate; // 配置波特率
husart.Init.WordLength = USART_WORDLENGTH_8B; // 配置数据有效位为 8bit
husart.Init.StopBits = USART_STOPBITS_1; // 配置一位停止位
husart.Init.Parity = USART_PARITY_NONE; // 不设校验位
husart.Init.Mode = USART_MODE_TX_RX; // 可收可发
husart.Init.HwFlowCtl = UART_HWCONTROL_NONE;//流控设置,没有用到( CTS/RTS),通常设置为None;
// 使用库函数初始化 USART1 的参数
if (HAL_UART_Init(&husart) != HAL_OK)
{
Error_Handler();
}
}
重定向打印函数 以上初始化完成后,就可以使用HAL库提供的“ HAL_UART_Transmit()” 从串口发送数据,使用 “ HAL_UART_Receive()”接收数据。 重定向打印函数, 在使用printf 时调用“ HAL_UART_Transmit()”打印
driver_usart.c
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&husart, (uint8_t*)&ch, 1, 10);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&husart, (uint8_t*)&ch, 1, 10);
return (int)ch;
}
printf和scanf会分别调用“ fputc()” 和“ fgetc()”,因此这里覆写这两个函数,使用HAL提供的函数实现收发数据。同时,还需要需要点击“ ”,打开工程选项界面,切换到“ Target”标签, 勾选上“ Use MicroLIB”,
?也可以添加如下代码,就不用勾选“ Use MicroLIB” ,两个方法二选其一即可
?driver_usart.c
/*
* 添加如下代码,可不在工程设置中勾选 Use MicroLIB
*/
#pragma import(__use_no_semihosting)
struct __FILE
{
int a;
};
FILE __stdout;
FILE __stdin;
void _sys_exit(int x)
{ }
中文打印测试;如果编译报错,需要点击“ ”,打开工程选项界面,切换到“C/C++”标签, 在“ Misc Controls”里加上“ "--locale=english"
?软件生成
?
?
?
?测试main.c
UsartInit(115200);
// 在 windows 下字符串\n\r 表示回车
// 如果工程在编译下面这句中文的时候报错,请在魔术棒“Option for target”->"C/C++"->"Misc Controls"添加“--locale=english”
printf("百问科技 www.100ask.net\n\r");
printf("UART 实验\n\r");
printf("test char = %c,%c\n\r", 'H', 'c');
printf("test string1 = %s\n\r", "www.100ask.net");
printf("test string2 = %s\n\r", "深圳百问网科技有限公司");
printf("test decimal1 number = %d\n\r", 123456);
printf("test decimal2 number = %d\n\r", -123456);
printf("test hex1 number = 0x%x\n\r", 0x123456);
printf("test hex2 number = 0x%08x\n\r", 0x123456);
printf("test float = %.5f\n\r", 3.1415);
printf("test double = %.10lf\n\r", 3.141592653);
printf("\r\n 键盘输入‘C’或者‘c’控制串口打印‘Hello world’");
while(1)
{
scanf("%c", &cmd);
if(cmd=='C' || cmd=='c')
{
cmd = 0;
printf("\r\nHello World.");
}
HAL_Delay(100);
}
模拟器运行 (芯片是stm32f103ze)
报错?
? error 65: access violation at 0x40022000 : no 'read' permission
没有配置好动态链接库
?
?填上
DARMSTM.DLL,-pSTM32F103ZE
?
|