一 原理讲解
1.1首先读模拟串口读前文档,
明白模拟串口怎么实现。用到的外设有普通gpio,外部中断,定时器。 本程序的代码在:
1.2 发送
串口发送每一个字节时候,每一个比特位占用多少时间, 如:波特率是38400,
- 一秒钟发送38400的比特位的数据,
- 每个字节占用10个比特位(1位起始位,8位数据位,无奇偶校验,1位停止位,)
- 每秒发送3840个字节, 每个比特位占用的时间是:1/38400秒= 26.04us
要实现一个us的延时函数,ST的HALL库默认是ms延时,这里在网上搜集了一下,用定时器实现us延时函数,顺便实现ms延时函数。
注意:这里要说明一下,HALL库的延时函数是在systick中断里面每ms加1s,没有延时的时候中断没有关闭,导致系统资源浪费。 正确的应该是给定时器1计数设置一个初始值,硬件自动加数,在delay里面读计数值,比如L431系统时钟是80MHz,定时器的配置如下:
定时器1每加1,时间是1us,我要延时26us,那我就再delay里面一直读定时器的CNT寄存器值是不是大于26。
1.3 接收
接收数据的时候,下降沿激发产生外部中断事件,开启定时器2。每隔一段时间读取一下gpio引脚的电平状态,保存在临时数组,到停止位则一个字节数据接收完成,读取字节数据,放在全局256大小的缓冲区。
注意:这里读取GPIo引脚的状态,应该在每个bit位的1/2读取GPIO的状态,这样读取的数据比较准确。
1.4 数据分包
有没有考虑过一个问题,怎么样收一包数据? 这里有个数据分包的问题,数据分包是这样的,我在数据停止位拿到数据以后,吧定时器2中断时间延长,我定义了两包数据之间的间隔大于100us,这里有两种情况:
- 后续没有数据到来,自然会进入到定时器2的分包处理流程。
- 后续有数据到来,外部中断重新设置定时器2时间。
二 crubeMX配置
2.1 GPIO配置
2.2 定时器2中断,外部中断
2.3 定时器1配置
2.4 定时器2配置
配置好后生成工程
三 代码
3.1 修改tim.c
实现定时器初始化,delay_ms(),delay_us(),定时器2回调
/* Includes ------------------------------------------------------------------*/
#include "tim.h"
/* USER CODE BEGIN 0 */
#include "myusart_RX.h"
/* USER CODE END 0 */
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
/* TIM1 init function */
void MX_TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 80-1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 1;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
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();
}
}
/* TIM2 init function */
void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 8-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_DOWN;
htim2.Init.Period = 130;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* TIM1 clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
/* USER CODE BEGIN TIM1_MspInit 1 */
/* USER CODE END TIM1_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspInit 0 */
/* USER CODE END TIM2_MspInit 0 */
/* TIM2 clock enable */
__HAL_RCC_TIM2_CLK_ENABLE();
/* TIM2 interrupt Init */
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/* USER CODE BEGIN TIM2_MspInit 1 */
/* USER CODE END TIM2_MspInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspDeInit 0 */
/* USER CODE END TIM1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM1_CLK_DISABLE();
/* USER CODE BEGIN TIM1_MspDeInit 1 */
/* USER CODE END TIM1_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspDeInit 0 */
/* USER CODE END TIM2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM2_CLK_DISABLE();
/* TIM2 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM2_IRQn);
/* USER CODE BEGIN TIM2_MspDeInit 1 */
/* USER CODE END TIM2_MspDeInit 1 */
}
}
下面是自己加的
/* USER CODE BEGIN 1 */
//实现微妙延时功能
void delay_us(uint16_t us)
{
uint16_t differ=0xffff-us-5;
HAL_TIM_Base_Start(&htim1);//打开定时器
__HAL_TIM_SetCounter(&htim1,differ);//设置计数初始值
while(differ < 0xffff-5)
{
differ = __HAL_TIM_GetCounter(&htim1);//拿到计数值
}
HAL_TIM_Base_Stop(&htim1);//关闭定时器
}
void delay_ms(uint16_t ms)
{
for(uint16_t i=0;i
{
delay_us(1000);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance == htim2.Instance)
{
TIM2_IRQHandlerHandle();
}
}
/* USER CODE END 1 */
3.2 修改gpio.c
实现引脚初始化,外部中断回调
#include "gpio.h"
/* USER CODE BEGIN 0 */
#include "myusart_RX.h"
/* USER CODE END 0 */
/*----------------------------------------------------------------------------*/
/* Configure GPIO */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/** Configure pins
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = usart_tx_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(usart_tx_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = usart_rx_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(usart_rx_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
下面是自己加的
/* USER CODE BEGIN 2 */
/**
* @brief EXIT中断回调函数
* @param GPIO_Pin —— 触发中断的引脚
* @retval none
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* 判断哪个引脚触发了中断 */
switch(GPIO_Pin)
{
case usart_rx_Pin:
GPIO_A_10_CALLBACK();
break;
case GPIO_PIN_3:
break;
default:
break;
}
}
/* USER CODE END 2 */
3.3 增加myusart_TX.c
实现发射功能
#include "myusart_TX.h"
#include "gpio.h"
#include "core_cm4.h"
#include "tim.h"
#define BuadRate_9600 100
#define BuadRate_38400 26
void delay_tx(void)//26.04us
{
delay_us(BuadRate_38400-1);
//补偿
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
}
void IO_TXD(uint8_t Data)
{
uint8_t i = 0;
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_RESET);
delay_tx();
for(i = 0; i < 8; i++)
{
if(Data&0x01)
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_RESET);
delay_tx();
Data = Data>>1;
}
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_SET);
delay_tx();
}
//定义的发送函数
void USART_Send(uint8_t *buf, uint8_t len)
{
uint8_t t;
for(t = 0; t < len; t++)
{
IO_TXD(buf[t]);
}
}
char *p="okokokokok";
void test_TX()
{
USART_Send((uint8_t *)p,10);
delay_ms(500);
}
3.4 增加myusart_TX.h
#ifndef __myusart_TX_H__
#define __myusart_TX_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
void test_TX(void);
void USART_Send(uint8_t *buf, uint8_t len);
#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */
一 原理讲解
1.1首先读模拟串口读前文档,
明白模拟串口怎么实现。用到的外设有普通gpio,外部中断,定时器。 本程序的代码在:
1.2 发送
串口发送每一个字节时候,每一个比特位占用多少时间, 如:波特率是38400,
- 一秒钟发送38400的比特位的数据,
- 每个字节占用10个比特位(1位起始位,8位数据位,无奇偶校验,1位停止位,)
- 每秒发送3840个字节, 每个比特位占用的时间是:1/38400秒= 26.04us
要实现一个us的延时函数,ST的HALL库默认是ms延时,这里在网上搜集了一下,用定时器实现us延时函数,顺便实现ms延时函数。
注意:这里要说明一下,HALL库的延时函数是在systick中断里面每ms加1s,没有延时的时候中断没有关闭,导致系统资源浪费。 正确的应该是给定时器1计数设置一个初始值,硬件自动加数,在delay里面读计数值,比如L431系统时钟是80MHz,定时器的配置如下:
定时器1每加1,时间是1us,我要延时26us,那我就再delay里面一直读定时器的CNT寄存器值是不是大于26。
1.3 接收
接收数据的时候,下降沿激发产生外部中断事件,开启定时器2。每隔一段时间读取一下gpio引脚的电平状态,保存在临时数组,到停止位则一个字节数据接收完成,读取字节数据,放在全局256大小的缓冲区。
注意:这里读取GPIo引脚的状态,应该在每个bit位的1/2读取GPIO的状态,这样读取的数据比较准确。
1.4 数据分包
有没有考虑过一个问题,怎么样收一包数据? 这里有个数据分包的问题,数据分包是这样的,我在数据停止位拿到数据以后,吧定时器2中断时间延长,我定义了两包数据之间的间隔大于100us,这里有两种情况:
- 后续没有数据到来,自然会进入到定时器2的分包处理流程。
- 后续有数据到来,外部中断重新设置定时器2时间。
二 crubeMX配置
2.1 GPIO配置
2.2 定时器2中断,外部中断
2.3 定时器1配置
2.4 定时器2配置
配置好后生成工程
三 代码
3.1 修改tim.c
实现定时器初始化,delay_ms(),delay_us(),定时器2回调
/* Includes ------------------------------------------------------------------*/
#include "tim.h"
/* USER CODE BEGIN 0 */
#include "myusart_RX.h"
/* USER CODE END 0 */
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
/* TIM1 init function */
void MX_TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 80-1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 1;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
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();
}
}
/* TIM2 init function */
void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 8-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_DOWN;
htim2.Init.Period = 130;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* TIM1 clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
/* USER CODE BEGIN TIM1_MspInit 1 */
/* USER CODE END TIM1_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspInit 0 */
/* USER CODE END TIM2_MspInit 0 */
/* TIM2 clock enable */
__HAL_RCC_TIM2_CLK_ENABLE();
/* TIM2 interrupt Init */
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/* USER CODE BEGIN TIM2_MspInit 1 */
/* USER CODE END TIM2_MspInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspDeInit 0 */
/* USER CODE END TIM1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM1_CLK_DISABLE();
/* USER CODE BEGIN TIM1_MspDeInit 1 */
/* USER CODE END TIM1_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspDeInit 0 */
/* USER CODE END TIM2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM2_CLK_DISABLE();
/* TIM2 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM2_IRQn);
/* USER CODE BEGIN TIM2_MspDeInit 1 */
/* USER CODE END TIM2_MspDeInit 1 */
}
}
下面是自己加的
/* USER CODE BEGIN 1 */
//实现微妙延时功能
void delay_us(uint16_t us)
{
uint16_t differ=0xffff-us-5;
HAL_TIM_Base_Start(&htim1);//打开定时器
__HAL_TIM_SetCounter(&htim1,differ);//设置计数初始值
while(differ < 0xffff-5)
{
differ = __HAL_TIM_GetCounter(&htim1);//拿到计数值
}
HAL_TIM_Base_Stop(&htim1);//关闭定时器
}
void delay_ms(uint16_t ms)
{
for(uint16_t i=0;i
{
delay_us(1000);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance == htim2.Instance)
{
TIM2_IRQHandlerHandle();
}
}
/* USER CODE END 1 */
3.2 修改gpio.c
实现引脚初始化,外部中断回调
#include "gpio.h"
/* USER CODE BEGIN 0 */
#include "myusart_RX.h"
/* USER CODE END 0 */
/*----------------------------------------------------------------------------*/
/* Configure GPIO */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/** Configure pins
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = usart_tx_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(usart_tx_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = usart_rx_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(usart_rx_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
下面是自己加的
/* USER CODE BEGIN 2 */
/**
* @brief EXIT中断回调函数
* @param GPIO_Pin —— 触发中断的引脚
* @retval none
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* 判断哪个引脚触发了中断 */
switch(GPIO_Pin)
{
case usart_rx_Pin:
GPIO_A_10_CALLBACK();
break;
case GPIO_PIN_3:
break;
default:
break;
}
}
/* USER CODE END 2 */
3.3 增加myusart_TX.c
实现发射功能
#include "myusart_TX.h"
#include "gpio.h"
#include "core_cm4.h"
#include "tim.h"
#define BuadRate_9600 100
#define BuadRate_38400 26
void delay_tx(void)//26.04us
{
delay_us(BuadRate_38400-1);
//补偿
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
}
void IO_TXD(uint8_t Data)
{
uint8_t i = 0;
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_RESET);
delay_tx();
for(i = 0; i < 8; i++)
{
if(Data&0x01)
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_RESET);
delay_tx();
Data = Data>>1;
}
HAL_GPIO_WritePin(usart_tx_GPIO_Port, usart_tx_Pin, GPIO_PIN_SET);
delay_tx();
}
//定义的发送函数
void USART_Send(uint8_t *buf, uint8_t len)
{
uint8_t t;
for(t = 0; t < len; t++)
{
IO_TXD(buf[t]);
}
}
char *p="okokokokok";
void test_TX()
{
USART_Send((uint8_t *)p,10);
delay_ms(500);
}
3.4 增加myusart_TX.h
#ifndef __myusart_TX_H__
#define __myusart_TX_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
void test_TX(void);
void USART_Send(uint8_t *buf, uint8_t len);
#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */
举报