串口(USART)
注意 需禁用syscalls.c
(和main.c在同一目录下)
替换为retarget
retarget.c位置在Core下的Src文件夹下
retarget.h位置在Core下的Inc文件夹下
使用方法
CUBEMX USART1 选择异步模式【Asynchronous】
/* USER CODE BEGIN 2*/
此处写代码(初始化函数后)
/*USER CODE END 2 */
RetargetInit(&huart1); //将printf()函数映射到USART1串口上
HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1);//开启串口1接收中断
while(1)
if(KEY_1()) //按键KEY1判断为1时按键按下
{
LED_1(1);//LED1灯控制(1点亮,0熄灭)
LED_2(1);//LED2灯控制(1点亮,0熄灭)
BUZZER_SOLO1();//蜂鸣器输出单音的报警音(样式1:HAL库的精准延时函数)
RELAY_1(1);//继电器的控制程序(c=0继电器放开,c=1继电器吸合)
HAL_UART_Transmit(&huart1,(uint8_t*)&"KEY1\r\n",6,0xffff);//串口发送:串口号1,内容"ABC",数量3,溢出时间0xffff
}
if(KEY_2()) //按键KEY2判断为1时按键按下
{
LED_1(0);//LED1灯控制(1点亮,0熄灭)
LED_2(0);//LED2灯控制(1点亮,0熄灭)
BUZZER_SOLO2();//蜂鸣器输出单音的报警音(样式2:CPU微秒级延时)
RELAY_1(0);//继电器的控制程序(c=0继电器放开,c=1继电器吸合)
printf("KEY2\r\n");//向USART1串口发送字符串
}
if(USART1_RX_STA==0x0001){//串口1判断中断接收标志位&0xC000(回车)==0x0001(不等待回车,判断标志位是否等于1)
if(USART1_RX_BUF[0]=='1'){
LED_1(1);//LED1灯控制(1点亮,0熄灭)
LED_2(1);//LED2灯控制(1点亮,0熄灭)
BUZZER_SOLO1();//蜂鸣器输出单音的报警音(样式1:HAL库的精准延时函数)
RELAY_1(1);//继电器的控制程序(c=0继电器放开,c=1继电器吸合)
}
if(USART1_RX_BUF[0]=='0'){
LED_1(0);//LED1灯控制(1点亮,0熄灭)
LED_2(0);//LED2灯控制(1点亮,0熄灭)
BUZZER_SOLO2();//蜂鸣器输出单音的报警音(样式2:CPU微秒级延时)
RELAY_1(0);//继电器的控制程序(c=0继电器放开,c=1继电器吸合)
}
USART1_RX_STA=0;//串口接收标志清0,即开启下一轮接收
}
usart.c
#include "usart.h"
uint8_t USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.
uint16_t USART1_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目
uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存
uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.
uint16_t USART2_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目
uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存
uint8_t RS485orBT;//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式
uint8_t USART3_RX_BUF[USART3_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.
uint16_t USART3_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目
uint8_t USART3_NewData;//当前串口中断接收的1个字节数据的缓存
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数
{
uint8_t a;
if(huart ==&huart1)//判断中断来源(串口1:USB转串口)
{
printf("%c",USART1_NewData); //把收到的数据以 a符号变量 发送回电脑
if((USART1_RX_STA&0x8000)==0){//接收未完成
if(USART1_RX_STA&0x4000){//接收到了0x0d
if(USART1_NewData!=0x0a)USART1_RX_STA=0;//接收错误,重新开始
else USART1_RX_STA|=0x8000; //接收完成了
}else{ //还没收到0X0D
if(USART1_NewData==0x0d)USART1_RX_STA|=0x4000;
else{
USART1_RX_BUF[USART1_RX_STA&0X3FFF]=USART1_NewData; //将收到的数据放入数组
USART1_RX_STA++; //数据长度计数加1
if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); //再开启接收中断
}
if(huart ==&huart2)//判断中断来源(RS485/蓝牙)
{
if(RS485orBT){//判断当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式
USART2_RX_BUF[0]=USART2_NewData;//收到数据放入缓存数组(只用到1个数据存放在数组[0])
USART2_RX_STA++;//数据接收标志位加1
HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData, 1); //再开启接收中断
}else{
if((USART2_RX_STA&0x8000)==0){//接收未完成(将USART2_RX_STA最高位1位规定是接收完成标志位)
if(USART2_NewData==0x5A)//如收到0x5A表示接收到结束符(手机APP“蓝牙调试器”回复数据以0x5A为结束符)
{
USART2_RX_STA|=0x8000;//收到0x0A,接受完成
}
else{ //如没有收到0x0A则继续接收数据内容并把数量加1
USART2_RX_BUF[USART2_RX_STA&0X7FFF]=USART2_NewData; //将收到的数据放入数组
USART2_RX_STA++; //数据长度计数加1
if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收
}
}
HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //再开启接收中断
}
}
if(huart ==&huart3)//判断中断来源(串口3:WIFI模块)
{
//【原始数据内容】字符:+IPD,1:A 十六进制:0D 0A 2B 49 50 44 2C 31 3A 41(其中1是数量,A是数据内容)
//【数据接收原理】当接收到0x0A(即“回车”中的“\r“)时触发接下来的数据采集程序,
//首先清空USART3_RX_BUF[]寄存器,然后将USART3_RX_STA的16位中最高位第2位置1(01000000 00000000)
//此时开始采集接下来收到的数据,当收到前6个数据是“+IPD,1:”,且第7个数据不等于0时,表示成功收完数据
//然后将接收的第7位的一个字节数据内容放入USART3_RX_STA寄存器低8位,并将16位中最高位置1(10000000 xxxxxxxx)。
//【调用方法】在主函数中用if语句判断(USART_RX_STA&0x8000),为真时表示成功收到数据。
//然后读USART_RX_STA寄存器低14位的内容(USART_RX_STA&0x3FFF),即是数据的内容(1个字节)。
//主函数处理完数据后要将USART_RX_STA清0,才能开启下一次数据接收。
if(USART3_RX_STA&0x4000){//判断开始标志位为1时(16位中高位第2位)进入数据采集处理
USART3_RX_BUF[USART3_RX_STA&0x3FFF]=USART3_NewData;//将
USART3_RX_STA++;
if(USART3_RX_BUF[0]=='+'&& //判断返回字符前几位是不是“+IPD,1:”
USART3_RX_BUF[1]=='I'&&
USART3_RX_BUF[2]=='P'&&
USART3_RX_BUF[3]=='D'&&
USART3_RX_BUF[4]==','&&
USART3_RX_BUF[5]=='1'&&//限定只接收1个数量的数据(可根据实际要求的数量修改)
USART3_RX_BUF[6]==':'&&
USART3_RX_BUF[7]!=0){ //同时判断第1个数据内容是否为0,为0表示还没有收到数据
USART3_RX_STA = USART3_RX_BUF[7]+0x8000;//将数据内容写入寄存器,16位最高位置1表示接收完成
}
}
if(USART3_NewData==0x0A && !(USART3_RX_STA&0x8000)){//判断是否收到“回车”中的“\r“(0x0A)
USART3_RX_STA=0x4000;//将开始采集标志位置1(16位中最高位第2位)
for(a=0;a<200;a++){//循环200次
USART3_RX_BUF[a]=0;//将数据寄存器清0
}
}
HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData,1); //再开启串口3接收中断
}
}
usart.h
#ifndef INC_USART_H_
#define INC_USART_H_
#include "stm32f1xx_hal.h" //HAL库文件声明
#include <string.h>//用于字符串处理的库
#include "../inc/retarget.h"//用于printf函数串口重映射
extern UART_HandleTypeDef huart1;//声明USART1的HAL库结构体
extern UART_HandleTypeDef huart2;//声明USART2的HAL库结构体
extern UART_HandleTypeDef huart3;//声明USART2的HAL库结构体
#define USART1_REC_LEN 200//定义USART1最大接收字节数
#define USART2_REC_LEN 200//定义USART1最大接收字节数
#define USART3_REC_LEN 200//定义USART1最大接收字节数
extern uint8_t USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern uint16_t USART1_RX_STA;//接收状态标记
extern uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存
extern uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern uint16_t USART2_RX_STA;//接收状态标记
extern uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存
extern uint8_t RS485orBT;//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式
extern uint8_t USART3_RX_BUF[USART3_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern uint16_t USART3_RX_STA;//接收状态标记
extern uint8_t USART3_NewData;//当前串口中断接收的1个字节数据的缓存
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口中断回调函数声明
#endif /* INC_USART_H_ */
retarget.c
#include <_ansi.h>
#include <_syslist.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/times.h>
#include <limits.h>
#include <signal.h>
#include <../Inc/retarget.h>
#include <stdint.h>
#include <stdio.h>
#if !defined(OS_USE_SEMIHOSTING)
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
UART_HandleTypeDef *gHuart;
void RetargetInit(UART_HandleTypeDef *huart) {
gHuart = huart;
/* Disable I/O buffering for STDOUT stream, so that
* chars are sent out as soon as they are printed. */
setvbuf(stdout, NULL, _IONBF, 0);
}
int _isatty(int fd) {
if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
return 1;
errno = EBADF;
return 0;
}
int _write(int fd, char* ptr, int len) {
HAL_StatusTypeDef hstatus;
if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
hstatus = HAL_UART_Transmit(gHuart, (uint8_t *) ptr, len, HAL_MAX_DELAY);
if (hstatus == HAL_OK)
return len;
else
return EIO;
}
errno = EBADF;
return -1;
}
int _close(int fd) {
if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
return 0;
errno = EBADF;
return -1;
}
int _lseek(int fd, int ptr, int dir) {
(void) fd;
(void) ptr;
(void) dir;
errno = EBADF;
return -1;
}
int _read(int fd, char* ptr, int len) {
HAL_StatusTypeDef hstatus;
if (fd == STDIN_FILENO) {
hstatus = HAL_UART_Receive(gHuart, (uint8_t *) ptr, 1, HAL_MAX_DELAY);
if (hstatus == HAL_OK)
return 1;
else
return EIO;
}
errno = EBADF;
return -1;
}
int _fstat(int fd, struct stat* st) {
if (fd >= STDIN_FILENO && fd <= STDERR_FILENO) {
st->st_mode = S_IFCHR;
return 0;
}
errno = EBADF;
return 0;
}
#endif //#if !defined(OS_USE_SEMIHOSTING)
retarget.h
#ifndef INC_RETARGET_H_
#define INC_RETARGET_H_
#include "stm32f1xx_hal.h"
#include "stdio.h"//用于printf函数串口重映射
#include <sys/stat.h>
void RetargetInit(UART_HandleTypeDef *huart);
int _isatty(int fd);
int _write(int fd, char* ptr, int len);
int _close(int fd);
int _lseek(int fd, int ptr, int dir);
int _read(int fd, char* ptr, int len);
int _fstat(int fd, struct stat* st);
#endif /* INC_RETARGET_H_ */