本节主要讲解串口1的收发通信功能,接收数据以换行符为结束标志(\r\n)。
串口的详细解说可参考《STM32F4XXX中文参考手册》,资料有详细的讲解和说明,文档可跟作者联系索取,或百度搜索也可。
话不多说,开始写代码吧。。
一、打开STM32CubeMX选择对应的芯片型号,后进入开发界面;
详细步骤可参考:
(1)<STM32>STM32CubeMX-工程创建及定时器控制LED灯闪烁(2)
(2)<STM32>STM32CubeMX-基础工程创建及LED灯控制 (1)
二、开始配置操作
1、配置RCC,即晶振的选择配置;
选择"Pinout&Configuration"->“System Core”->“RCC”,如下图步骤1、2、3;
High Speed Clock(HSE):表示外部高速晶振;
High Speed Clock(HSE):表示外部低速晶振,使用RTC实时时钟是一般用这个;
在步骤4、5可选择晶振的配型,“BYPASS Clock Source”和“Crystal/Ceramic Resonator”两种,第一个是旁路时钟源,第二个是晶体/陶瓷谐振器,一般电路使用的是“Crystal/Ceramic Resonator”。
本教程作者选用的高速和低速都是“Crystal/Ceramic Resonator”。
2、配置SYS,即DEBUG的方式选择
选择"Pinout&Configuration"->“System Core”->“SYS”,如下图步骤1、2、3;
下图步骤4则是选择Debug的方式,一般选择SDW的方式能满足使用,如果想用JTAG也可选择JTAG。
本教程在这里作者选中SDW的方式即“Serial Wire”。
3、配置串口参数
本次使用的是串口1,串口1默认引脚是PA9和PA10;
配置参数入上图所示:
步骤1,2,3:选择到对应的配置串口,选择后出现中间部分框图;
步骤4:可选择项分为一下几种:
1)Asynchronous:异步通信
2)synchronous :同步通信
3)single Wire(Half-Duplex):单线(半双工)通信
4)Multiprocessor Communication:多处理器通信
5)LrDA :红外通信
6)LIN :LIN通信
7)SmartCard:智能卡
本次教程选用Asynchronous:异步通信即可,主要是做普通串口使用;
步骤5:串口参数配置:
步骤6:参数设置,包含:波特率:115200、字长:8位、校验位:无、停止位:1位;
步骤7:数据方向:接收和发送 、 16位采样;
步骤8:勾选中断使能
三、工程配置
1、时钟配置:
如上图所示:选择 步骤1选项,然后再步骤2位置输入系统的主频时钟值,回车确认即可,自动完成。F4系列一般是 168或180,F1系列一般是72.
2、工程输出属性配置
工程属性配置,如上图所示:
步骤1、2:进入配置界面;
步骤3:输入工程名称;
步骤4:工程存放的目录路径;
步骤5:工程的DMK 和对应的版本选择;使用Keil5的选择”MDK-ARM“
步骤6:堆栈大小的设置,一般用的不大的话,使用默认即可。
3、代码生成器配置
代码生成器配置如上图:
步骤1、2:进入配置界面;
步骤3:拷贝所以使用到的库到工程中;
步骤4:生成单独的.c和.h文件,方便后续功能添加修改管理;
建议初学者按上图配置即可。
四、代码生成和功能补充测试
1、代码生成
在以上所有都配置完成后,电机上图 红色1位置,即可生成代码,并在弹出的对话框选择“Open Project”打开项目。
2、代码补充
在打开的工程中补充修改完善代码;
补充修改代码分别在4个文件中进行,根据配置后生成的工程目录如下图所示:
补充修改完善代码分别对应的文件是“main.c”、“stm32f4xx_it.c”、“usart.c"和"usart.h”,这四个文件,下面分别说明具体修改内容。
1)"stm32f4xx_it.c"文件修改:主要是注释掉,自动生成的串口中断函数,按找下图注释掉,具体如下:
2)"usart.h"文件修改:主要是添加一些定义等,具体如下所示:
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN Private defines */
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define RXBUFFERSIZE 1 //缓存大小
extern uint8_t USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern uint16_t USART_RX_STA; //接收状态标记
/* USER CODE END Private defines */
void MX_USART1_UART_Init(void);
/* USER CODE BEGIN Prototypes */
uint8_t API_USART1_SendData(uint8_t *pData,uint16_t len);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __USART_H__ */
3)"usart.c"修改:主要是定义变量、增加中断处理函数、中断公共回调函数、以及重定向输出函数(用以使用printf),还有一个则是在初始化函数内添加接收中断buf。
变量定义如下:
/* USER CODE BEGIN 0 */
#include "stdio.h"
uint8_t USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
uint16_t USART_RX_STA=0; //接收状态标记
uint8_t aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲
/* USER CODE END 0 */
初始化函数内添加接收中断buf:如下:
void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE);//开启接收数据
/* USER CODE END USART1_Init 2 */
}
中断函数如下:
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint32_t timeout=0;
uint32_t maxDelay=0x1FFFF;
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
timeout=0;
while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY)//等待就绪
{
timeout++;超时处理
if(timeout>maxDelay) break;
}
timeout=0;
while(HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
{
timeout++; //超时处理
if(timeout>maxDelay) break;
}
/* USER CODE END USART1_IRQn 1 */
}
中断公共回调函数如下:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)//如果是串口1
{
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
发送函数如下:
uint8_t API_USART1_SendData(uint8_t *pData,uint16_t len)
{
uint8_t ret = 0;
if(HAL_UART_Transmit(&huart1,pData,len,5000) != HAL_OK)
{
ret = 1;
}
return ret;
}
重定向输出如下:
//重定向输出,可用printf函数
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
**代码放置位置,建议放在有标识的位置如“USER CODE BEGIN X”和“USER CODE END X”,其中X代表数值1.2.3…;将代码放置于这两句话之间的效果就是,如果重新用STM32CubeMX重新配置这个工程,那么在这两句话之间书写的代码,不会被清理掉,会保留;如果在之两句话之外的位置书写代码,则会被清理掉。**本次工程放置效果如下图所示:
4)“main.c”文件修改:主要是对输入输出数据进行操作,具体如下:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint16_t RevLen;
uint8_t i;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(USART_RX_STA&0x8000) //判断接收到数据
{
RevLen = USART_RX_STA&0x3fff;//获取接收数据的长度
printf("接收数据长度为:%d \r\n",RevLen); //打印输出长度
printf("接收数据内容为:%s \r\n",USART_RX_BUF);//打印输出接收的数据(字符串的形式)
USART_RX_STA=0; //清除接收的标志
memset(USART_RX_BUF,0,USART_REC_LEN);//清除接收的内容
}
i++;
if(i>=10) //循环10次
{
i=0;
printf("请输入数据,以回车键结束\r\n");
}
HAL_Delay(100); //延时100ms
}
/* USER CODE END 3 */
}
3、编译代码和下载运行
1)编译:编译后无错误,如下图所示,如果有错误,根据错误修改错误的位置,然后重新编译,直至编译成功。
2)下载:点击下载按钮,等待下载完成即可;如下图:
4、测试
将板子的PA9和PA10 接到串口转TTL模块,并将模块的USB接口接到电脑,然后打开串口调试助手(网上有很多可以下载,或者可以联系作者索要),根据模块识别的串口号,打开对应的串口,注意波特率、数据为、停止位、校验位等参数,要与代码设置的参数保持一致。 有些开发板可能会带有串口转TTL芯片,有的,则直接将对应接口的USB口,接到电脑即可(根据具体使用的开发板情况而定)。
打开串口后,在输入框输入发送的数据如:“123abc”,注意勾选“换行”,并点击发送;可在接收窗口看待对应输出的输出信息;
如下图:
至此串口收发功能以实现;
如有不理解、不明白指出可联系作者;
如有错误遗漏等,还望指教说明,多促进交流;
联系方式:QQ759521350
…本文属原创作品,转载表明出处…