[STM32F103C8T6] 串口

1.非中断串口发送数据

 

 

 

串口发送/接收函数:

HAL_UART_Transmit(); 串口发送数据,使用超时管理机制

HAL_UART_Receive(); 串口接收数据,使用超时管理机制

HAL_UART_Transmit_IT(); 串口中断模式发送  

HAL_UART_Receive_IT(); 串口中断模式接收

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

作用:以阻塞的方式发送指定字节的数据

形参 1 :UART_HandleTypeDef 结构体类型指针变量

形参 2:指向要发送的数据地址

形参 3:要发送的数据大小,以字节为单位

形参 4:设置的超时时间,以ms单位

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

作用:以中断的方式接收指定字节的数据

形参 1 是 UART_HandleTypeDef 结构体类型指针变量

形参 2 是指向接收数据缓冲区

形参 3 是要接收的数据大小,以字节为单位

此函数执行完后将清除中断,需要再次调用以重新开启中断。

 代码实现

#include <stdio.h>
#include <string.h>
unsigned char ch[20] = {0};
int fputc(int ch, FILE *f)
{
unsigned char temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,0xffff);
return ch;
}
main函数里:
unsigned char ch[20] = {0};
HAL_UART_Transmit(&huart1, "hello world\n", strlen("hello world\n"), 100);
while(1)
{
HAL_UART_Receive(&huart1, ch, 19, 100);//因为字符串最后一位是'\0'所以20个字符只接收19个
//HAL_UART_Transmit(&huart1, ch, strlen(ch), 100);
printf(ch);
memset(ch, 0, strlen(ch));
}

int fputc(int ch, FILE *f)
{
unsigned char temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,0xffff);
return ch;
}

这段代码的意思是将C语言的库函数printf重定向,或重映射,这词有点太专业,简单的讲:
就是C语言的库中printf函数会调用PUTCHAR_PROTOTYPE这个硬件接口函数,向显示器上输出char型变量,用来显示,
STM32当然是没有显示器的,所以就用UART输出Char型变量到PC机,由PC机的超级终端来显示,说白了,
就是原来送到显示器上的显示数据,现在送到STM32的串口上;
如果调不过去,你要注意两点:
1.你的板子上用的是不是USART1,如果是,那连到PC的串口,就可以在超级终端上,看到打印出来的东西了,如果不是,请参考第2点.
2.先将你的板子上的USART初始化,然后把PUTCHAR_PROTOTYPE函数体中的USART1换成你现在用的那个USART.

很多人在用printf函数进行串口打印的时候,都会被告知需要重定向fputc函数(别的平台可能不是这个函数),让字符串数据输出到指定串口,按照网上的教程也能很快解决。但是却没人告诉你为什么可以被重定向,为什么明明使用的是printf函数,重定向的却是fputc函数?

因为weak!!!!

printf()函数一般是将数据输出到屏幕上显示的,为什么这里可以将数据通过printf从串口发出呢?

因为:printf 最终会调用 fputc 进行字符串输出,但是这些函数是标准库提供的,而标准库没有提供源码给你,当你需要用的时候添加 <stdio.h> 即可,但是很多时候,fputc 输出的位置可能需要改变,比如输出到 LCD、串口1、串口2,我们总不可能去修改标准库的源码吧,但也没有源码提供啊,怎么才能在不修改源码的情况下满足这个需求呢?

方法是有的,比如你可以通过某个函数向printf中注册一个回调函数,让printf调用这个回调函数进行字符串输出即可,但是标准库并没有提供这个东西,因为它用了更好的方式解决这个问题,重写fputc函数,让fputc函数输出字符通过再通过串口发送即可

为什么可以重写呢?

         在 HAL 库中,很多回调函数前面使用__weak 修饰符。
weak 顾名思义是“弱”的意思,所以如果函数名称前面加上__weak 修饰符,我们一般称这个函数为“弱函数”。
加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,
那么编译器就会执行__weak 声明的函数,并且编译器不会报错。

2.中断串口发送数据 

怎么判断是uart1产生的中断 

 查看回调函数,并自己重写回调函数

#include <stdio.h>
//串口接收缓存(1字节)
uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART1_RX_STA=0;
// 接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 判断中断是由哪个串口触发的
if(huart->Instance == USART1)
{
// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
if((UART1_RX_STA & 0x8000) == 0)
{
// 如果已经收到了 0x0d (回车),
if(UART1_RX_STA & 0x4000)
{
// 则接着判断是否收到 0x0a (换行)
if(buf == 0x0a)
// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
UART1_RX_STA |= 0x8000;
else
// 否则认为接收错误,重新开始
UART1_RX_STA = 0;
}
else // 如果没有收到了 0x0d (回车)
{
//则先判断收到的这个字符是否是 0x0d (回车)
if(buf == 0x0d)
{
// 是的话则将 bit14 位置为1
UART1_RX_STA |= 0x4000;
}
else
{
// 否则将接收到的数据保存在缓存数组里
UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
UART1_RX_STA++;
// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
if(UART1_RX_STA > UART1_REC_LEN - 1)
UART1_RX_STA = 0;
}
}
}
// 重新开启中断
HAL_UART_Receive_IT(&huart1, &buf, 1);
}
}
int fputc(int ch, FILE *f)
{
unsigned char temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,0xffff);
return ch;
}
main函数部分
HAL_UART_Receive_IT(&huart1, &buf, 1);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//判断判断串口是否接收完成
if(UART1_RX_STA & 0x8000)
{
printf("收到数据:");
// 将收到的数据发送到串口
HAL_UART_Transmit(&huart1, UART1_RX_Buffer, UART1_RX_STA & 0x3fff, 0xffff);
// 等待发送完成
while(huart1.gState != HAL_UART_STATE_READY);
printf("\r\n");
// 重新开始下一次接收
UART1_RX_STA = 0;
}
printf("hello liangxu\r\n");
HAL_Delay(1000);
}

调试程序时,发现的几个错误

1.判断条件成立为真不一定是等于1

如果写成这样就会出错:

 2.接收来自上位机的数据是用UART1_RX_STA的bit0-bit13位接收

所以接收数据的语句应该是:

 千万不要写成:

UART1_REC_LEN是自己定义的数组长度名,接收数据时光标是 UART1_RX_STA,接收一位往后移一位