本篇文章参照借鉴该连接有兴趣的朋友可以去看看:
------------------- 点击 跳转连接 -------------------------
下见串口示例图:
下面是每一位数据在波特不同的情况数据电平持续时间:
当波特率为9600时: 发送1bit数据需要:1/9600 = 104us
当波特率为19200时: 发送1bit数据需要:1/19200= 58us
当波特率为115200时: 发送1bit数据需要: 1/19200= 8.68us
这几个波特率用的多我就列出来了
#define IO_USART_SENDDELAY_TIME 104 //9600 波特率计算值
//枚举类型标记当前位
enum{
COM_START_BIT,
COM_D0_BIT,
COM_D1_BIT,
COM_D2_BIT,
COM_D3_BIT,
COM_D4_BIT,
COM_D5_BIT,
COM_D6_BIT,
COM_D7_BIT,
COM_STOP_BIT,
};
//GPIO TX脚宏定义
#define iouart1_TXD(n) if(n) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); \
else HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
//GPIO RX引脚宏定义
#define iouart1_RXD HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)
uint8_t recvData = 0; //接收数据
uint8_t recvStat = COM_STOP_BIT; //接收状态
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* 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_TIM1_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim3); //打开循环Time 用于循环发送串口数据
HAL_TIM_Base_Start(&htim2); //打开Time2 用于计数 卡发送时间
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(system_flag.bits.system_1ms == 1) //system loop 1ms
{
system_flag.bits.system_1ms = 0;
}
if(system_flag.bits.system_10ms == 1) //system loop 10ms
{
system_flag.bits.system_10ms = 0;
}
if(system_flag.bits.system_100ms == 1) //system loop 10ms
{
system_flag.bits.system_100ms = 0;
}
//以上的判定可以忽略
if(system_flag.bits.system_500ms == 1) //周期循环500ms
{
system_flag.bits.system_500ms = 0;
printf("12111111\r\n"); //串口打印数据
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/***************************************
* 函数名: Delay_Ms
* 功能说明: 延时
* 形参:nTime,单位为uS
* 返回值: 无
****************************************/
void iouart1_delayUs(volatile uint32_t nTime)
{
uint16_t tmp;
tmp = __HAL_TIM_GetCounter(&htim2); //获得 TIM2 计数器的值
if(tmp + nTime <= 65535)
while( (__HAL_TIM_GetCounter(&htim2)-tmp) < nTime );
else
{
__HAL_TIM_SET_COUNTER(&htim2, 0);//设置 TIM3 计数器寄存器值为0
while( __HAL_TIM_GetCounter(&htim2) < nTime );
}
}
#define IO_USART_SENDDELAY_TIME 104 这个值就是上面计算出来的宏定义
/*****************************************
* 函数名: iouart1_SendByte
* 功能说明: 模拟串口发送1字节数据
* 形参:datatoSend
* 返回值: 无
******************************************/
void iouart1_SendByte(uint8_t datatoSend)
{
uint32_t i, tmp;
// 开始位
iouart1_TXD(0); //将TXD的引脚的电平置低
iouart1_delayUs(IO_USART_SENDDELAY_TIME);
for(i = 0; i < 8; i++)
{
tmp = (datatoSend >> i) & 0x01;
if(tmp == 0)
{
iouart1_TXD(0);
iouart1_delayUs(IO_USART_SENDDELAY_TIME); //0
}
else
{
iouart1_TXD(1);
iouart1_delayUs(IO_USART_SENDDELAY_TIME); //1
}
}
// 结束位
iouart1_TXD(1);//将TXD的引脚的电平置?
iouart1_delayUs(IO_USART_SENDDELAY_TIME);
}
/*****************************************
* 函数名: iouart1_SendByte
* 功能说明: 模拟串口发送多字节数据
* 形参:SendData
* 返回值: 无
******************************************/
void iouart1_sendBuff(const char *SendData)
{
for(int i=0;i<strlen(SendData);i++)
{
iouart1_SendByte(SendData[i]);
}
}
/*****************************************
* 函数名: MX_TIM1_Init
* 功能说明: Tim1初始化
* 形参:无
* 返回值: 无
******************************************/
void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 99;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = IO_USART_SENDDELAY_TIME; //这里是上面宏定义时间,每次中断到达了就进行数据数据
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
}
/*****************************************
* 函数名: MX_GPIO_Init
* 功能说明: GPIO初始化
* 形参:无
* 返回值: 无
******************************************/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
/*Configure GPIO pin : PB6 */
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
/* USER CODE BEGIN 2 */
//************************************************************************
这里以上的都是软件生成不用去管他,下面是串口接受数据时外部中断触发 就起始位进入时处理的
//*************************************************************************
/*****************************************
* 函数名: HAL_GPIO_EXTI_Callback
* 功能说明: GPIO中断回调函数
* 形参:GPIO_Pin
* 返回值: 无
******************************************/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin==GPIO_PIN_7)
{
if(iouart1_RXD == 0)
{
if(recvStat == COM_STOP_BIT)
{
recvStat = COM_START_BIT;
//这要延时下 跳过起始位等待
iouart1_delayUs(50);
//开启Time1中断计数
HAL_TIM_Base_Start_IT(&htim1);
}
}
}
/*****************************************
* 函数名: HAL_TIM_PeriodElapsedCallback
* 功能说明: TIME中断回调函数
* 形参:htim
* 返回值: 无
******************************************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint8_t system10ms_cnt =0;
static uint8_t system100ms_cnt =0;
static uint16_t system500ms_cnt =0;
if(htim->Instance == TIM3) //如果是TIM3 触发中断
{
system_flag.bits.system_1ms = 1;
if((++system10ms_cnt) >= 10)
{
system10ms_cnt =0;
system_flag.bits.system_10ms = 1;
}
if((++system100ms_cnt) >= 100)
{
system100ms_cnt =0;
system_flag.bits.system_100ms = 1;
}
if((++system500ms_cnt) >= 500)
{
system500ms_cnt =0;
system_flag.bits.system_500ms = 1;
}
}
else if(htim->Instance == TIM1) //如果是TIM1 触发中断
{
recvStat++;
if(recvStat == COM_STOP_BIT)
{
HAL_TIM_Base_Stop_IT(&htim1);
//到这就接收到完整的1个字节数据
recvData = recvData;
return;
}
if(iouart1_RXD)
{
recvData |= (1 << (recvStat - 1));
}else{
recvData &= ~(1 << (recvStat - 1));
}
}
}
下面还是要讲一下 重定义发送函数 用来打印状态处理, 如果你想用printf 函数打印数据,拿小本本记一下
#include "stdio.h"
#include "string.h"
/*****************************************
* 函数名: fputc
* 功能说明: 重定义函数
* 形参:ch,f
* 返回值: int
******************************************/
int fputc(int ch, FILE *f)
{
uint16_t count = 0;
iouart1_sendBuff((unsigned char *)&ch); //调用打印串函数
return ch;
}
空闲检测: 加一个累计计数的标志位,当接受数据停止的时候( 检测到 COM_STOP_BIT)进行累计计数 ,超过设定阈值就认为进入了空闲状态。
队列接受存储数据,加入数组或者指针实现也行。
串口错误帧检测,也是加一个累计计数标志位,从接受数据开始计算,如果没有检测到 COM_STOP_BIT 并且当前计数已经超过阈值,就 判定位错误帧。