通信RS232
- 初始化USART1、2、3:设置波特率,收发选择,有效数据位等;
- 将所使用的串口引脚初始化:USART使能、GPIO端口时钟使能、GPIO引脚设置为USART复用;
- RS232采用中断方式发送,编写中断回调函数;
- 主函数编写控制逻辑:使用MobaXterm向调试串口发送“A/a”,公头发送一次数据给母头,成功则绿灯亮,失败则红灯亮;向调试串口发送“B/b”,母头发送一次数据给公头,成功则蓝灯亮,失败则红灯亮;向调试串口发送“C/c”,自动间隔一秒,公母互发一次数据,成功则绿/蓝灯闪烁,失败则红灯亮;向调试串口发送“C/c”,停止自动发送,红灯亮;
软件设计
用到三个串口,USART1用于调试、USART2用于RS232公头、USART3用于RS232母头。
创建好所需.c和.h文件。
Step1:先给一些引脚、时钟等宏定义
从调式串口USART1开始:相关宏定义在文件driver_usart.h中
#ifndef __DRIVER_USART1_H
#define __DRIVER_USART1_H
#include "stm32f1xx_hal.h" //hal库头文件,提供访问底层硬件的接口,如GPIO、USART
/*********************
* 引脚宏定义
**********************/
#define DEBUG_USART USART1
#define DEBUG_USART_RX_PIN GPIO_PIN_10
#define DEBUG_USART_TX_PIN GPIO_PIN_9
#define DEBUG_USART_PORT GPIOA
#define DEBUG_USART_GPIO_CLK_EN() __HAL_RCC_GPIOA_CLK_ENABLE()
#define DEBUG_USART_CLK_EN() __HAL_RCC_USART1_CLK_ENABLE()
#define DEBUG_USART_CLK_DIS() __HAL_RCC_USART1_CLK_DISABLE()
#define DEBUG_USART_IRQn USART1_IRQn
/*********************
* 函数宏定义
**********************/
/*********************
* 全局变量声明
**********************/
extern UART_HandleTypeDef husart1;
/*********************
* 对外函数API
**********************/
/*
* 函数名:void DEBUG_USART_Init(uint32_t baudrate)
* 输入参数:baudrate-串口波特率
* 输出参数:无
* 返回值:无
* 函数作用:初始化USART的波特率,收发选择,有效数据位等
*/
extern void DEBUG_USART_Init(uint32_t baudrate);
#endif
再到RS232公头USART2相关宏定义driver_usart2.h
#ifndef __DRIVER_USART2_H
#define __DRIVER_USART2_H
#include "stm32f1xx_hal.h"
/*********************
* 引脚宏定义
**********************/
#define RS232_MALE USART2
#define RS232_MALE_RX_PIN GPIO_PIN_3
#define RS232_MALE_TX_PIN GPIO_PIN_2
#define RS232_MALE_PORT GPIOA
#define RS232_MALE_GPIO_CLK_EN() __HAL_RCC_GPIOA_CLK_ENABLE()
/*********************
* 函数宏定义
**********************/
#define RS232_MALE_IRQn USART2_IRQn
#define RS232_MALE_IRQHandler USART2_IRQHandler
#define RS232_MALE_CLK_ENABLE() __HAL_RCC_USART2_CLK_ENABLE()
#define RS232_MALE_CLK_DISABLE() __HAL_RCC_USART2_CLK_DISABLE()
/*********************
* 全局变量声明
**********************/
extern UART_HandleTypeDef husart2;
extern volatile int8_t male_tx_finish;
extern volatile int8_t male_rx_finish;
/*********************
* 对外函数API
**********************/
/*
* 函数名:void UsartInit(uint32_t baudrate)
* 输入参数:baudrate-串口波特率
* 输出参数:无
* 返回值:无
* 函数作用:初始化USART的波特率,收发选择,有效数据位等
*/
extern void RS232_MALE_Init(uint32_t baudrate);
/*
* 函数名:void RS232_MALE_Tx(uint8_t *pdata, uint16_t sz)
* 输入参数:pdata->指向发送数据所存储的首地址
sz->发送数据个数
* 输出参数:无
* 返回值:无
* 函数作用:USART2的发送函数
*/
extern void RS232_MALE_Tx(uint8_t *pdata, uint16_t sz);
/*
* 函数名:void RS232_MALE_Rx(uint8_t *pdata, uint16_t sz)
* 输入参数:pdata->指向接收数据所存储的首地址
sz->接收数据个数
* 输出参数:无
* 返回值:无
* 函数作用:USART2的接收函数
*/
extern void RS232_MALE_Rx(uint8_t *pdata, uint16_t sz);
#endif
最后是RS232母头USART3相关宏定义driver_usart3.h
#ifndef __DRIVER_USART3_H
#define __DRIVER_USART3_H
#include "stm32f1xx_hal.h"
/*********************
* 引脚宏定义
**********************/
#define RS232_FEMALE USART3
#define RS232_FEMALE_RX_PIN GPIO_PIN_11
#define RS232_FEMALE_TX_PIN GPIO_PIN_10
#define RS232_FEMALE_PORT GPIOB
#define RS232_FEMALE_GPIO_CLK_EN() __HAL_RCC_GPIOB_CLK_ENABLE()
/*********************
* 函数宏定义
**********************/
#define RS232_FEMALE_IRQn USART3_IRQn
#define RS232_FEMALE_IRQHandler USART3_IRQHandler
#define RS232_FEMALE_CLK_ENABLE() __HAL_RCC_USART3_CLK_ENABLE()
#define RS232_FEMALE_CLK_DISABLE() __HAL_RCC_USART3_CLK_DISABLE()
/*********************
* 全局变量声明
**********************/
extern UART_HandleTypeDef husart3;
extern volatile int8_t female_tx_finish;
extern volatile int8_t female_rx_finish;
/*********************
* 对外函数API
**********************/
/*
* 函数名:void RS232_FEMALE_Init(uint32_t baudrate)
* 输入参数:baudrate-串口波特率
* 输出参数:无
* 返回值:无
* 函数作用:初始化USART的波特率,收发选择,有效数据位等
*/
extern void RS232_FEMALE_Init(uint32_t baudrate);
/*
* 函数名:void RS232_FEMALE_Tx(uint8_t *pdata, uint16_t sz)
* 输入参数:pdata->指向发送数据所存储的首地址
sz->发送数据个数
* 输出参数:无
* 返回值:无
* 函数作用:USART3的发送函数
*/
extern void RS232_FEMALE_Tx(uint8_t *pdata, uint16_t sz);
/*
* 函数名:void RS232_FEMALE_Rx(uint8_t *pdata, uint16_t sz)
* 输入参数:pdata->指向接收数据所存储的首地址
sz->接收数据个数
* 输出参数:无
* 返回值:无
* 函数作用:USART3的接收函数
*/
extern void RS232_FEMALE_Rx(uint8_t *pdata, uint16_t sz);
#endif
Step2:初始化USART
分为两个部分:协议部分和硬件部分放在各自“.c”文件里,硬件部分都是调用“HAL_UART_Init()”,单独创建一个“.c”文件处理。
先配置协议部分
USART1初始化driver_usart.c:
#include <stdio.h>
#include "main.h"
#include "driver_usart1.h"
/*
* 定义全局变量
*/
UART_HandleTypeDef husart1; //这定义了一个全局变量 husart1,它是一个 UART_HandleTypeDef 类型的变量。UART_HandleTypeDef 是一个结构体,用于存储UART(通用异步收发传输器)的句柄和配置信息
/*
* 函数名:void DEBUG_USART_Init(uint32_t baudrate)
* 输入参数:baudrate-串口波特率
* 输出参数:无
* 返回值:无
* 函数作用:初始化USART的波特率,收发选择,有效数据位等
*/
void DEBUG_USART_Init(uint32_t baudrate)
{
husart1.Instance = DEBUG_USART; // 选择USART1
husart1.Init.BaudRate = baudrate; // 配置波特率
husart1.Init.WordLength = USART_WORDLENGTH_8B; // 配置数据有效位为8bit
husart1.Init.StopBits = USART_STOPBITS_1; // 配置一位停止位
husart1.Init.Parity = USART_PARITY_NONE; // 不设校验位
husart1.Init.Mode = USART_MODE_TX_RX; // 可收可发
husart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
// 使用库函数初始化USART3的参数
if (HAL_UART_Init(&husart1) != HAL_OK)
{
Error_Handler();
}
}
/*****************************************************
*function: 写字符文件函数
*param1: 输出的字符
*param2: 文件指针
*return: 输出字符的ASCII码
******************************************************/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&husart1, (uint8_t*)&ch, 1, 10);
return ch;
}
/*****************************************************
*function: 读字符文件函数
*param1: 文件指针
*return: 读取字符的ASCII码
******************************************************/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&husart1, (uint8_t*)&ch, 1, 10);
return (int)ch;
}
USART2初始化(driver_usart2.c)
#include "main.h"
#inclide "driver_usart2.h"
UART_HandleTypeDef husart2;
volatile int8_t male_tx_finish = 0;
volatile int8_t male_rx_finish = 0;
void RS232_MALE_Init(uint32_t baudrate)
{
husart2.Instance = RS232_MALE;
husart2.Init.BaudRate = baudrate;
husart2.Init.WordLength = USART_WORDLENGTH_8B;
husart2.Init.StopBits = USART_STOPBIT_1;
husart2.Init.Parity = USART_PARITY_NONE; // 不设校验位
husart2.Init.Mode = USART_MODE_TX_RX; // 可收可发
husart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
if(HAL_UART_Init(&husart2) !=HAL_OK)
{
Error_Handler();
}
void RS232_MALE_IRQHandler(void)
{
HAL_UART_IRQHandler(&husart2);
}
void RS232_MALE_Tx(uint8_t *pdata, uint16_t sz)
{
male_tx_finish = 0;
HAL_UART_Transmit_IT(&husart2, pdata, sz);
}
void RS232_MALE_Rx(uint8_t *pdata, uint16_t sz)
{
male_rx_finish = 0;
HAL_UART_Receive_IT(&husart2, pdata, sz);
}
}
USART3初始化driver_usart3.c
#include "main.h"
#include "driver_usart3.h"
UART_HandleTypeDef husart3;
volatile int8_t female_tx_finish = 0;
volatile int8_t female_rx_finish = 0;
void RS232_FEMALE_Init(uint32_t baudrate)
{
husart3.Instance = RS232_FEMALE; // 选择USART3
husart3.Init.BaudRate = baudrate; // 配置波特率
husart3.Init.WordLength = USART_WORDLENGTH_8B; // 配置数据有效位为8bit
husart3.Init.StopBits = USART_STOPBITS_1; // 配置一位停止位
husart3.Init.Parity = USART_PARITY_NONE; // 不设校验位
husart3.Init.Mode = USART_MODE_TX_RX; // 可收可发
husart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
if (HAL_UART_Init(&husart3) != HAL_OK)
{
Error_Handler();
}
}
void RS232_FEMALE_IRQHandler(void)
{
HAL_UART_IRQHandler(&husart3);
}
void RS232_FEMALE_Tx(uint8_t *pdata, uint16_t sz)
{
female_tx_finish = 0;
HAL_UART_Transmit_IT(&husart3, pdata, sz);
}
void RS232_FEMALE_Rx(uint8_t *pdata, uint16_t sz)
{
female_rx_finish = 0;
HAL_UART_Receive_IT(&husart3, pdata, sz);
}
硬件部分
#include "driver_usart1.h"
#include "driver_usart2.h"
#include "driver_usart3.h"
#include "driver_msp_usart.h"
void HAL_UART_MspInit(UART_HandleTypeDef* husart)
{
// 定义GPIO结构体对象
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(husart->Instance==DEBUG_USART)
{
// 使能USART1的时钟
DEBUG_USART_CLK_EN();
// 使能USART1的输入输出引脚的时钟
DEBUG_USART_GPIO_CLK_EN();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = DEBUG_USART_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(DEBUG_USART_PORT, &GPIO_InitStruct); // 初始化TX引脚
GPIO_InitStruct.Pin = DEBUG_USART_RX_PIN; // 选择RX引脚
GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT; // 配置为输入
HAL_GPIO_Init(DEBUG_USART_PORT, &GPIO_InitStruct); // 初始化RX引脚
HAL_NVIC_SetPriority(DEBUG_USART_IRQn, 1, 0); // 设置USART3的中断等级(0-15)(0-15)
// 规则:(0,0)最高,(0,1)次之依次由高到低排序到(15,15)
HAL_NVIC_EnableIRQ(DEBUG_USART_IRQn); // 使能USART1的中断
}
else if(husart->Instance==RS232_MALE)
{
// 使能USART2的时钟
RS232_MALE_CLK_ENABLE();
// 使能USART2的输入输出和方向引脚的时钟
RS232_MALE_GPIO_CLK_EN();
/**USART3 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = RS232_MALE_TX_PIN; // 选择USART2的TX引脚
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 配置为复用推挽功能
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 引脚翻转速率快
HAL_GPIO_Init(RS232_MALE_PORT, &GPIO_InitStruct); // 初始化TX引脚
GPIO_InitStruct.Pin = RS232_MALE_RX_PIN; // 选择RX引脚
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 配置为输入
GPIO_InitStruct.Pull = GPIO_NOPULL; // 不上拉
HAL_GPIO_Init(RS232_MALE_PORT, &GPIO_InitStruct); // 初始化RX引脚
HAL_NVIC_SetPriority(RS232_MALE_IRQn, 1, 0); // 设置USART3的中断等级(0-15)(0-15)
// 规则:(0,0)最高,(0,1)次之依次由高到低排序到(15,15)
HAL_NVIC_EnableIRQ(RS232_MALE_IRQn); // 使能USART2的中断
}
else if(husart->Instance==RS232_FEMALE)
{
// 使能USART3的时钟
RS232_FEMALE_CLK_ENABLE();
// 使能USART3的输入输出引脚的时钟
RS232_FEMALE_GPIO_CLK_EN();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
*/
GPIO_InitStruct.Pin = RS232_FEMALE_TX_PIN; // 选择USART3的TX引脚
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 配置为复用推挽功能
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 引脚翻转速率快
HAL_GPIO_Init(RS232_FEMALE_PORT, &GPIO_InitStruct); // 初始化TX引脚
GPIO_InitStruct.Pin = RS232_FEMALE_RX_PIN; // 选择RX引脚
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 配置为输入
GPIO_InitStruct.Pull = GPIO_NOPULL; // 不上拉
HAL_GPIO_Init(RS232_FEMALE_PORT, &GPIO_InitStruct); // 初始化RX引脚
HAL_NVIC_SetPriority(RS232_FEMALE_IRQn, 1, 0); // 设置USART3的中断等级(0-15)(0-15)
// 规则:(0,0)最高,(0,1)次之依次由高到低排序到(15,15)
HAL_NVIC_EnableIRQ(RS232_FEMALE_IRQn); // 使能USART3的中断
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* husart)
{
if(husart->Instance==DEBUG_USART)
{
DEBUG_USART_CLK_DIS();
HAL_GPIO_DeInit(DEBUG_USART_PORT, DEBUG_USART_TX_PIN | DEBUG_USART_RX_PIN);
HAL_NVIC_DisableIRQ(DEBUG_USART_IRQn);
}
else if(husart->Instance==RS232_MALE)
{
RS232_MALE_CLK_DISABLE();
HAL_GPIO_DeInit(RS232_MALE_PORT, RS232_MALE_TX_PIN | RS232_MALE_RX_PIN);
HAL_NVIC_DisableIRQ(RS232_MALE_IRQn);
}
else if(husart->Instance==RS232_FEMALE)
{
__HAL_RCC_USART3_CLK_DISABLE();
HAL_GPIO_DeInit(RS232_FEMALE_PORT, RS232_FEMALE_TX_PIN | RS232_FEMALE_RX_PIN);
HAL_NVIC_DisableIRQ(RS232_FEMALE_IRQn);
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == RS232_MALE)
{
male_tx_finish = 1;
}
else if(huart->Instance == RS232_FEMALE)
{
female_tx_finish = 1;
}
}
/*
* 函数名:void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
* 输入参数:husart-USART句柄
* 输出参数:无
* 返回值:无
* 函数作用:USART接收中断回调函数
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == RS232_MALE)
{
male_rx_finish = 1;
}
else if(huart->Instance == RS232_FEMALE)
{
female_rx_finish = 1;
}
}
还有一个LED的.c和.h和之前一样
最后到main.c
#include <stdio.h>
#include "main.h"
#include "driver_led.h"
#include "driver_usart1.h"
#include "driver_usart2.h"
#include "driver_usart3.h"
#include "driver_msp_usart.h"
volatile uint8_t step = 0;
SYSTICK gSYSTICK = {0};
static uint8_t RS232_MALE_TxMsg = 0x55;
static uint8_t RS232_MALE_RxMsg = 0;
static uint8_t RS232_FEMALE_TxMsg = 0xAA;
static uint8_t RS232_FEMALE_RxMsg = 0;
int main(void)
{
char cmd = 0;
int cnt = 1;
// 初始化HAL库函数必须要调用此函数
HAL_Init();
/*
* 系统时钟即AHB/APB时钟配置
* 使用外部高速时钟HSE(8MHz)配置系统时钟,经过PLL放大9倍,得到72MHz
*/
SystemClock_Config();
// 初始化USART1,设置波特率为115200 bps
DEBUG_USART_Init(115200);
// 初始化USART2,设置波特率为9600 bps
RS232_MALE_Init(9600);
// 初始化USART3,设置波特率为9600 bps
RS232_FEMALE_Init(9600);
// 初始化LED
LedGpioInit();
// 在windows下字符串\r\n表示回车
// 如果工程在编译下面这句中文的时候报错,请在“Option for target”->"C/C++"->"Misc Controls"添加“ --locale=english”
printf("百问科技www.100ask.net\n\r");
printf("RS232实验\n\r");
printf("输入指令: \n\r \
'A/a'->RS232公头发送指令给RS232母头,成功绿灯亮,失败红灯亮 \n\r \
‘B/b’->RS232母头发送指令给RS232公头,成功蓝灯亮,失败红灯亮 \n\r \
‘C/c’->自动间隔1S,RS232公母自动发送,成功绿/蓝灯亮,失败红灯亮 \n\r \
'E/e'->停止测试\n\r");
while(1)
{
scanf("%c", &cmd); // 获取按键输入
if(cmd != 0)
{
if(cmd=='A' || cmd=='a') // 输入按键A/a
{
step = 1;
printf("第%2d次:RS232公头---->RS232母头\n\r", cnt);
cnt++;
RS232_FEMALE_Rx((uint8_t*)&RS232_FEMALE_RxMsg, 1); // 母头等待接收
RS232_MALE_Tx((uint8_t*)&RS232_MALE_TxMsg, 1); // 公头发送数据
}
else if(cmd == 'B' || cmd=='b') // 输入按键B/a
{
step = 2;
printf("第%2d次:RS232公头<----RS232母头\n\r", cnt);
cnt++;
RS232_MALE_Rx((uint8_t*)&RS232_MALE_RxMsg, 1); // 公头等待接收
RS232_FEMALE_Tx((uint8_t*)&RS232_FEMALE_TxMsg, 1); // 母头发送数据
}
else if(cmd=='C' || cmd=='c') // 输入按键C/c
{
step = 3;
//因为要循环发送,需要放在主循环里实现
}
else if(cmd=='E' || cmd=='e') // 输入按键E/e
{
step = 0;
printf("停止测试\n\r");
}
cmd = 0;
}
// 自动间隔1S,RS232公母自动发送
if (step == 3)
{
if(gSYSTICK.rs232_female_period == 0) // 滴答定时器周期到了
{
gSYSTICK.rs232_female_period = 1000; // 重新装填初始值1000ms
printf("第%2d次:RS232公头---->RS232母头\n\r", cnt);
cnt++;
RS232_FEMALE_Rx((uint8_t*)&RS232_FEMALE_RxMsg, 1);
RS232_MALE_Tx((uint8_t*)&RS232_MALE_TxMsg, 1);
}
if(gSYSTICK.rs232_male_period == 0) // 滴答定时器周期到了
{
gSYSTICK.rs232_male_period = 1000; // 重新装填初始值1000ms
printf("第%2d次:RS232公头<----RS232母头\n\r", cnt);
cnt++;
RS232_MALE_Rx((uint8_t*)&RS232_MALE_RxMsg, 1);
RS232_FEMALE_Tx((uint8_t*)&RS232_FEMALE_TxMsg, 1);
}
}
// 分别判断RS232公母接收结果,点亮对应LED灯
if(female_rx_finish && step == 1) // 判断RS232公头->RS232母头是否成功
{
male_rx_finish = 0;
RLED(OFF);
GLED(ON);
BLED(OFF);
}
if(male_rx_finish && step == 2) // 判断RS232母头->RS232公头是否成功
{
female_rx_finish = 0;
RLED(OFF);
GLED(OFF);
BLED(ON);
}
if(male_rx_finish && female_rx_finish && step == 3) // 判断RS232自动互传是否成功
{
RLED(OFF);
GLED(ON);
BLED(ON);
}
if ((male_rx_finish == 0
&& female_rx_finish == 0) || step == 0) // 失败或者退出测试
{
RLED(ON);
GLED(OFF);
BLED(OFF);
}
}
}
/*
* 函数名:void Error_Handler(void)
* 输入参数:无
* 输出参数:无
* 返回值:无
* 函数作用:程序错误处理函数,此处暂时设为死循环,不做任何动作
*/
void Error_Handler(void)
{
while(1)
{
}
}
main.h
#ifndef __MAIN_H
#define __MAIN_H
#include "stm32f1xx_hal.h"
#include "stm32f1xx_clk.h"
typedef struct
{
uint32_t timeout; // 超时时间
uint32_t rs232_female_period; // RS232母头自动发送周期
uint32_t rs232_male_period; // RS232公头自动发送周期
}SYSTICK;
extern void Error_Handler(void);
extern volatile uint8_t step;
extern SYSTICK gSYSTICK;
#endif