前言
本篇博文将会介绍如何配置串口收发所需要的配置,而后会介绍两种方式实现串口收发。
在开始之前需要知道的:
串口过采样模式:
8倍过采样
16倍过采样
使用比奈奎斯特采样定理更高的的采样率,会消耗更大的功耗得到更加准确的结果,比如使用16倍过采样接受一位数据实际上采样了16个点。
阻塞操作:
是指在执行设备操作时,若不能获得资源则挂起进程,直到满足可操作的条件后进行操作,被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作:
进程不能进行设备操作时并不挂起,他或者放弃,或者不停的查询,直到可以进行操作为止。
1、系统配置
SYS配置:
RCC配置:
USART配置:
选择异步收发模式:
串口配置:
115200波特率、8位数据位、无奇偶校验、1位停止位
收发模式、16位过采样
NVIC配置:
Pinout view:
时钟树配置:
2、实现收发
2.1、重写函数实现printf、scanf、getchar函数的使用
现在usart.c文件的最下方将fputc和fgetc函数重写,而后才可使用printf、scanf、getchar函数。
/* USER CODE BEGIN 0 */
#include
/* USER CODE END 0 */
...
/* USER CODE BEGIN PD */
#define RXBUFFERSIZE 256
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
char RxBuffer[RXBUFFERSIZE];
/* USER CODE END PV */
...
/* USER CODE BEGIN 1 */
//重定向c库函数printf到DEBUG_USARTx
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
return ch;
}
//重定向c库函数getchar,scanf到DEBUG_USARTx
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 1000);
return ch;
}
/* USER CODE END 1 */
main.c
#include "stdio.h"
#include "string.h"
...
int main(void)
{
/* USER CODE BEGIN 1 */
char Str[255];
/* 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 */
StrInit(Str,strlen(Str));
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
scanf("%s", Str);
if(Str[0] == 'a')
{
printf("Hello CubeMX!n");
}
else if(Str[0] == 'b')
{
getchar();//接收回车
Str[0] = getchar();
if(Str[0] == 'c')
{
printf("Hello World!n");
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
...
void StrInit(char *StringInit,int16_t Len)
{
int16_t i = 0;
for(i = 0; i < Len; i++)
{
StringInit
= 0; } }
实现效果图:
2.2、调用HAL库函数实现串口收发 2.2.1、串口收发函数一览 (1)HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); (2)HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); (3)HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); (4)HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); (5)HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); (6)HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); (7)HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart); (8)HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart); (9)HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart);
2.2.2、串口收发函数解析 在这里只介绍用到的两个库函数(1)和(4):
(1)HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); 1 功能描述: 以阻塞模式发送大量数据。 参量: huart:指向包含指定UART模块的配置信息的UART_HandleTypeDef结构的指针 pData:指向发送数据缓冲区的指针 Size: 要发送的数据字节数 Timeout:持续时间 返回值: HAL status,如果发送超时返回HAL_TIMEOUT
(4)HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); 1 功能描述: 在非阻塞模式下接收大量数据。 参量: huart:指向包含指定UART模块的配置信息的UART_HandleTypeDef结构的指针 pData:指向接收数据缓冲区的指针 Size: 要接收的数据数量 返回值: HAL status,如果发送超时返回HAL_TIMEOUT
2.2.3、串口收发函数的使用 在接下来的实验中将实现串口发送数据回显功能。
串口发送函数: usart.c:
... /* USER CODE BEGIN 1 */ //串口1发送字符串 void UsartSendString(uint8_t *str) { unsigned int k=0; do { HAL_UART_Transmit(&huart1,(uint8_t *)(str + k) ,1,1000); k++; } while(*(str + k)!=' '); } /* USER CODE END 1 */ ...
串口接收操作: 串口接受的操作相对来说较为复杂,实现过程可以用下面的流程图来描述。
所以我们要做的就是先在初始化阶段调用中断接收函数告诉串口将发来的下一字节存储到指定区域然后在回调函数中对接收的数据进行处理然后再次调用中断接收函数来告诉串口将发来的下一字节存储到指定区域。 main.c:
... /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define RXBUFFERSIZE 256 //最大接收字节数
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
char RxBuffer[RXBUFFERSIZE]; //接收数据
uint8_t RxITBuffer; //接收中断缓冲
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
/* USER CODE END PV */
...
/* USER CODE BEGIN 2 */
memset(RxBuffer,0x00,sizeof(RxBuffer)); //初始化串口接收数据缓冲区
HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxITBuffer, 1);//开启接收中断
/* USER CODE END 2 */
...
/* USER CODE BEGIN 4 */
//重写串口中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(Uart1_Rx_Cnt >= 255) //溢出判断
{
Uart1_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,1000);
}
else
{
RxBuffer[Uart1_Rx_Cnt++] = RxITBuffer; //接收数据转存
if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位 0x0A--->r 0x0D--->n
{
UsartSendString((uint8_t *)RxBuffer); //将收到的信息发送回去
Uart1_Rx_Cnt = 0; //计数变量清零
memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxITBuffer, 1); //再开启接收中断
}
/* USER CODE END 4 */
验证截图:
|