理论:
串口采取异步通信,即不依赖时钟节拍来接收或发送数据,而是采用互相约定的波特率传输数据。
波特率与单位时间传输的比特数有关,波特率越大传输的数据越多
传输一个比特花费的时间T = 1 / 比特率
接受和发送数据的时候需要接受端和发送端:
UART传输数据是一位一位的向接收端传输,为了知道何时传输,又或者为了保证传输数据的完整正确性,规定了传输协议:
分别为开始、数据、校验、停止,其中校验位可要可不要
传输样例:
停止位取二进制数0,数据位一般有8位刚好能装下一个字符,一般的停止位是1位取值为1,校验一般不要
开发板原理图:
可以出开发板有两组收发引脚,但其实真正有效的只有一组即PA2和PA3,下面详细说一下:
上述是开发板中控制下载调试器部分的芯片,其一般作用就是将程序或固件加载到微处理器或微控制器,朴素的讲调试器主要作用就是将电脑代码导入到开发板的芯片中
也就是说下载调试器能连接电脑,STM32L071
要想能与电脑上的串口小助手通信,那就必须TX,RX能与电脑相连,所以下载调试器中的芯片GD32F350C8T6
上的PA9、PA10就必须先发送或者接收STM32L071
传来的数据,其真正的作用是一个连接作用,即将STM32L071
与电脑间接相连
而STM32L071的另外一组引脚没有与下载调试器相连所以也就没有作用
STM32L071
的TX,RX与GD32F350C8T6
引脚连接的部分:
而SET由开发板SELECT按键控制其高低电平,高低电平能使其接通1、4引脚或3、4引脚
CubMX配置:
Asynchronous: 异步通信
Keil配置:
Function.c和Function.h文件:
#include "Function.h"
#include "i2c.h"
#include "oled.h"
#include "usart.h"
void OLED_Write(unsigned char type, unsigned char data){ // 写函数
unsigned char Write_data[2];
Write_data[0] = type;
Write_data[1] = data;
HAL_I2C_Master_Transmit(&hi2c3, 0x78, Write_data, 2, 0xff);
}
void Function_OledEnable(unsigned char ms){ // Oled使能
HAL_GPIO_WritePin(OLED_POWER_GPIO_Port, OLED_POWER_Pin, GPIO_PIN_RESET);
HAL_Delay(ms);
OLED_Init();
}
void Function_SendInfromation(const char * data, uint16_t len){ // 发送信息
HAL_UART_Transmit(&huart2, data, len, 0xff);
}
void Function_ReceiveInfromation(char * data, uint16_t len){ // 接受信息
HAL_UART_Receive(&huart2, data, len, 0xff);
}
#ifndef __FUNCTION__
#define __FUNCTION__
#include <stdint.h>
void OLED_Write(unsigned char type, unsigned char data);
void Function_OledEnable(unsigned char ms);
void Function_ReceiveInfromation(char * data, uint16_t len);
void Function_SendInfromation(const char * data, uint16_t len);
#endif
main.c
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "Function.h"
#include "oled.h"
#include <string.h>
void SystemClock_Config(void);
int main(void)
{
char a[] = {4, 5, 6};
char flag = 1;
char b[] = {0, 0, 0};
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C3_Init();
MX_USART2_UART_Init();
Function_OledEnable(50);
while (1)
{
OLED_ShowString(0, 0, "i am wining", 16);
if(flag <= 2){
flag ++;
HAL_Delay(5000);
Function_SendInfromation(a, strlen(a));
HAL_Delay(5000);
}
OLED_ShowString(0, 2, "Receive:", 16);
Function_ReceiveInfromation(b, 3);
if(b[0] == '@'){
OLED_ShowString(64, 2, b, 16);
b[0] = '1';
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_4;
RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_I2C3;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
PeriphClkInit.I2c3ClockSelection = RCC_I2C3CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
效果:
程序启动后等10s钟后发送数据:
拓展:
HAL_UART_Transmit
与HAL_UART_Receive
,在传输与接收数据的时候,都会对做一些检查,例如指针数据长度是否有效,收发是否有效,如果不合格就会返回错误,如果合格就会进入忙碌状态即利用while函数一直对数据进行转发,或者收取。
在接受数据的时候,如果未能及时接收,那么后到的数据回应超时未接收报错,然后终止操作,这就是为什么在主函数有程序抢占cpu时不能即时接收数据,最后显示的数据只有一个字符。
这种情况可以用中断串口通信解决。