一、设计用途:

公司PCB制成板降成本,选择的MCU比项目需求少一个串口,为满足制成板成本和项目对串口需求,选择模拟一路串口。

二、硬件电路:

三、设计实现:

工具&软件:STM32F030R8    KEIL5    STM32CubeMX

1、 串口通信

串口是一种很常用的通信接口,按位(bit)发送和接收字节,串口通信是异步传输,端口能够在一根线上发送数据同时在另一根线上接收数据。串口通信最重要的参数是比特率数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配,在我们单片机设计中发送和接收一个字节的位格式一般由起始信号位,数据位,停止位组成,很少有校验位,但是可以有。

2、 串口STM32串口硬件实现原理

既然想要模拟串口通信,那就需要设计得最接近于硬件串口,那么硬件串口是怎样实现数据的采集的呢?

以设置过采样率为8时进行分析:

在硬件串口接收线空闲时,串口一般会以一定频率去采集接收线上电平信号,如果辨认出一个特殊序列,1110X0X0X0X0X0X0 则确认接收到起始帧,触发接收中断。在前面判断三个1后如果后面采集到的位中有1则会降噪声位置1,所以在模拟串口时也需要加入噪声处理。

硬件串口的噪声处理:

简单来说,噪声处理就是将采集到的正中间的三位值进行判定,取三个中至少两个相同值作为最终采集值。

3、 实现方式选择

在网上大概看了一些前辈做的模拟串口,大致分为以下几种:

1、 阻塞式:接收使用中断,发送直接在主函数中进行,接收时以波特率的位时间进行定时采集,尽量的将采集点位于位中间。

2、 非阻塞式:使用一个定时器或者两个定时器,将接收和发送作于中断中,但接收方式基本一致,都只采集一次,未将噪声考虑进入。

也许每个行业对噪声的关注度都不太一样,但是在我这项目中噪声确是一个不得不面对的一个问题,只为通信更可靠。

自写第一种实现方式:

以波特率位时间的1/6进行中断采集,将每一位均分六等份,采集五次,然后取中间三次进行值判断。

#include "UartSet.h"
#include "string.h" uint8_t ucRecvData = ; //每次接收的字节
uint8_t ucAcquCx = ; //三次滤波计数
uint8_t ucRecvBitCx = ; //接收位计数
uint8_t ucSendBitCx = ; //发送位计数
uint8_t ucSendLengCx = ; //发送长度计数 uint8_t ucRecvLastBit = ; //上次接收位记录
uint8_t ucRecvNowBit = ; //当前位记录
uint8_t ucRecvTrueCx = ; //正确计数 uint32_t Prescaler = ;
uint32_t Period = ;
UART gsUARTBuff; //定义串口 TIM_HandleTypeDef htim6; void MX_TIM6_Init(void)
{ __HAL_RCC_TIM6_CLK_ENABLE(); htim6.Instance = TIM6;
htim6.Init.Prescaler = Prescaler;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = Period;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_NVIC_SetPriority(TIM6_IRQn, , );
HAL_NVIC_EnableIRQ(TIM6_IRQn); HAL_TIM_Base_Init(&htim6); } void UART_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {}; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = COM_TX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(COM_TX_GPIO_Port, &GPIO_InitStruct); GPIO_InitStruct.Pin = COM_RX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(COM_RX_GPIO_Port, &GPIO_InitStruct); HAL_NVIC_SetPriority(EXTI4_15_IRQn, , );
HAL_NVIC_EnableIRQ(EXTI4_15_IRQn); } /*******************************************************************************
* @FunctionName : UART_Init.
* @Description : 模拟串口结构体初始化.
* @Input : None.
* @Output : None.
* @Return : None.
* @Author&Data : MrShuCai 2019.4.11.
*******************************************************************************/
void UART_Init(void)
{
gsUARTBuff.CheckType = NONE;
gsUARTBuff.UartMaxLength = Uartlength;
gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
UART_GPIO_Init(); if(BaudRate == )
{
Prescaler = ;
Period = ;
}
else if(BaudRate == )
{
Prescaler = ;
Period = ;
}
else if(BaudRate == )
{
// Prescaler = 15;
// Period = 50; //
Prescaler = ;
Period = ;
} MX_TIM6_Init();
} /*******************************************************************************
* @FunctionName : UART_Send_Data.
* @Description : 模拟串口发送数据接口.
* @Input : None.
* @Output : None.
* @Return : None.
* @Author&Data : MrShuCai 2019.4.11.
*******************************************************************************/
void UART_Send_Data(uint8_t * data, uint8_t size)
{
gsUARTBuff.Sendlength = size;
memcpy(gsUARTBuff.UART_Send_buf, data, size);
gsUARTBuff.TxEn = ;
gsUARTBuff.RxEn = ;
gsUARTBuff.UartStat = COM_START_BIT_DEAL;
HAL_TIM_Base_Start_IT(&htim6);
} /*******************************************************************************
* @FunctionName : EXTI4_15_IRQHandler.
* @Description : 接收引脚外部中断,下降沿触发,触发后即进入起始位判断.
* @Input : None.
* @Output : None.
* @Return : None.
* @Author&Data : MrShuCai 2019.4.11.
*******************************************************************************/
void EXTI4_15_IRQHandler(void)
{ if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_4) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4); if(gsUARTBuff.UartStat == COM_NONE_BIT_DEAL)
{
gsUARTBuff.RxEn = ;
ucRecvData = ;
gsUARTBuff.UartStat = COM_START_BIT_DEAL;
HAL_TIM_Base_Start_IT(&htim6);
}
}
} /*******************************************************************************
* @FunctionName : TIM6_IRQHandler.
* @Description : 中断处理函数,包括发送和接收两部分.
* @Input : None.
* @Output : None.
* @Return : None.
* @Author&Data : MrShuCai 2019.4.11.
*******************************************************************************/
void TIM6_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim6); if(gsUARTBuff.TxEn == ) /*数据发送,发送优先,无发送后才进入接收状态*/
{
switch(gsUARTBuff.UartStat) /*串口发送位状态判断*/
{
case COM_START_BIT_DEAL :
{
HAL_GPIO_WritePin(COM_TX_GPIO_Port, COM_TX_Pin, GPIO_PIN_RESET); if(++ ucAcquCx == ) /*由于发送时在立即拉低就进入判断,且自加,所以需要加到4,实际延时3个周期*/
{
gsUARTBuff.UartStat = COM_DATA_BIT_DEAL;
ucAcquCx = ;
ucSendBitCx = ;
}
}
break; case COM_DATA_BIT_DEAL :
{ HAL_GPIO_WritePin(COM_TX_GPIO_Port, COM_TX_Pin, (GPIO_PinState)((gsUARTBuff.UART_Send_buf[ucSendLengCx] >> ucSendBitCx) & 0x01)); if(++ ucAcquCx >= )
{
ucAcquCx = ; ucSendBitCx ++; if(ucSendBitCx >= )
{
if(gsUARTBuff.CheckType == NONE)
{
gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
}
else
{
gsUARTBuff.UartStat = COM_CHECK_BIT_DEAL;
}
}
}
}
break; case COM_CHECK_BIT_DEAL :
{ }
break; case COM_STOP_BIT_DEAL :
{
HAL_GPIO_WritePin(COM_TX_GPIO_Port, COM_TX_Pin, GPIO_PIN_SET); if(++ ucAcquCx == )
{
ucAcquCx = ;
ucSendBitCx = ; if(ucSendLengCx < gsUARTBuff.Sendlength - )
{
gsUARTBuff.UartStat = COM_START_BIT_DEAL;
ucSendLengCx ++;
}
else
{
ucSendLengCx = ;
gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
gsUARTBuff.TxEn = ;
gsUARTBuff.RxEn = ; HAL_TIM_Base_Stop_IT(&htim6);
TIM6->CNT = ;
}
}
}
break; default:
break; }
} if(gsUARTBuff.RxEn == )
{
switch(gsUARTBuff.UartStat)
{
case COM_START_BIT_DEAL : //起始位 必须连续三次采集正确才认可
{
ucRecvLastBit = ucRecvNowBit;
ucRecvNowBit = HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin); if( ucAcquCx == )
{
ucAcquCx = ;
return ;
} if(ucRecvLastBit == ucRecvNowBit)
{
ucRecvTrueCx ++;
} if(++ ucAcquCx >= )
{
if(ucRecvTrueCx >= )
{
gsUARTBuff.UartStat = COM_DATA_BIT_DEAL;
ucAcquCx = ;
ucRecvTrueCx = ;
}
else
{
ucAcquCx = ;
ucRecvTrueCx = ;
gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
}
}
}
break; case COM_DATA_BIT_DEAL : //数据位
{ ucRecvLastBit = ucRecvNowBit;
ucRecvNowBit = HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin); if( == ucAcquCx)
{
ucAcquCx = ;
return;
} if(ucRecvNowBit == ucRecvLastBit)
{
ucRecvTrueCx ++;
} if(++ ucAcquCx >= )
{
ucAcquCx = ; if(ucRecvTrueCx >= )
{
if(ucRecvLastBit == )
{
ucRecvData |= ( << ucRecvBitCx);
}
else
{
ucRecvData &= ~( << ucRecvBitCx);
} if(ucRecvBitCx >= )
{
ucRecvBitCx = ; if(gsUARTBuff.CheckType == NONE)
{
gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
}
else
{
gsUARTBuff.UartStat = COM_CHECK_BIT_DEAL;
}
}
else
{
ucRecvBitCx ++;
}
}
else
{
ucAcquCx = ;
ucRecvTrueCx = ;
gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
}
}
}
break; case COM_CHECK_BIT_DEAL : //校验位
{ }
break; case COM_STOP_BIT_DEAL : //停止位
{ ucRecvLastBit = ucRecvNowBit;
ucRecvNowBit = HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin); if( == ucAcquCx)
{
ucAcquCx = ;
return;
} if(ucRecvNowBit == ucRecvLastBit && ucRecvNowBit == )
{
ucRecvTrueCx ++;
} if( ++ ucAcquCx >= )
{
ucAcquCx = ; if(ucRecvTrueCx >= )
{
ucRecvTrueCx = ;
if(gsUARTBuff.Recvlength < gsUARTBuff.UartMaxLength)
{
gsUARTBuff.UART_Recv_buf[gsUARTBuff.Recvlength] = ucRecvData;
gsUARTBuff.Recvlength ++;
} gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
HAL_TIM_Base_Stop_IT(&htim6);
TIM6->CNT = ;
}
else
{
ucRecvTrueCx = ;
}
}
}
break; default:
break; }
} }

在使用中已经达到要求,无错误识别,但是由于在电路中使用了光耦,所以波形存在一定形变,不能及时将电平立即拉低,同时中断次数还是比较频繁的,为减少误判和减少中断次数,采取另一种方式。

4、 最优实现

减少中断次数,每位只需中断三次,同时为避免光耦导致的滞后,将前面部分过滤不采集,只在中间快速采集三次,动态实现定时器定时时间调整。

比如在4800波特率下,一位208us,则定时器从起始位开始,定时序列为80,20,20,168,20,20,168,20,20,168......这样的序列。168为前一位的最后定时时间加当前位的前80us时间,也就是上图的起始位的4位置到第0位的2位置的时间。

#include "UartSet.h"
#include "string.h" typedef enum
{
TimeRecvStartStep1 = ,
TimeRecvStep2 = ,
TimeRecvStep3 = ,
TimeRecvStep1 = ,
TimeSendStep = , } TimeStep; uint16_t TimeSet[]; uint16_t TimeSetBuff[][] = {{, , , , }, //
{, , , , }, //
{, , , , }, //
{, , , , }, //
{, , , , }, //预留
}; UART gsUARTBuff; //定义串口 uint8_t ucRecvData = ; //每次接收的字节
uint8_t ucAcquCx = ; //三次滤波计数
uint8_t ucRecvBitCx = ; //接收位计数
uint8_t ucSendBitCx = ; //发送位计数
uint8_t ucSendLengCx = ; //发送长度计数 uint8_t ucRecvBitBuff = ; //采集位保存 TIM_HandleTypeDef htim6; void MX_TIM6_Init(void)
{ __HAL_RCC_TIM6_CLK_ENABLE(); htim6.Instance = TIM6;
htim6.Init.Prescaler = ;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = TimeSet[TimeRecvStartStep1];
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_NVIC_SetPriority(TIM6_IRQn, , );
HAL_NVIC_EnableIRQ(TIM6_IRQn); HAL_TIM_Base_Init(&htim6); } void UART_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {}; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = COM_TX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(COM_TX_GPIO_Port, &GPIO_InitStruct); GPIO_InitStruct.Pin = COM_RX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(COM_RX_GPIO_Port, &GPIO_InitStruct); HAL_NVIC_SetPriority(EXTI4_15_IRQn, , );
HAL_NVIC_EnableIRQ(EXTI4_15_IRQn); } /*******************************************************************************
* @FunctionName : UART_Init.
* @Description : 模拟串口结构体初始化.
* @Input : None.
* @Output : None.
* @Return : None.
* @Author&Data : MrShuCai 2019.4.11.
*******************************************************************************/
void UART_Init(void)
{
gsUARTBuff.CheckType = NONE;
gsUARTBuff.UartMaxLength = Uartlength;
gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
UART_GPIO_Init(); if(BaudRate == )
{
memcpy(TimeSet, &TimeSetBuff[][], sizeof(TimeSet));
}
else if(BaudRate == )
{
memcpy(TimeSet, &TimeSetBuff[][], sizeof(TimeSet));
}
else if(BaudRate == )
{
memcpy(TimeSet, &TimeSetBuff[][], sizeof(TimeSet));
}
else if(BaudRate == )
{
memcpy(TimeSet, &TimeSetBuff[][], sizeof(TimeSet));
}
else
{ } MX_TIM6_Init();
} /*******************************************************************************
* @FunctionName : UART_Send_Data.
* @Description : 模拟串口发送数据接口.
* @Input : None.
* @Output : None.
* @Return : None.
* @Author&Data : MrShuCai 2019.4.11.
*******************************************************************************/
void UART_Send_Data(uint8_t * data, uint8_t size)
{
gsUARTBuff.Sendlength = size;
memcpy(gsUARTBuff.UART_Send_buf, data, size); if(gsUARTBuff.UartStat == COM_NONE_BIT_DEAL)
{
gsUARTBuff.TxEn = ;
gsUARTBuff.RxEn = ;
gsUARTBuff.UartStat = COM_START_BIT_DEAL; TIM6->ARR = TimeSet[TimeSendStep];
TIM6->EGR = TIM_EGR_UG; HAL_TIM_Base_Start_IT(&htim6);
} } /*******************************************************************************
* @FunctionName : EXTI4_15_IRQHandler.
* @Description : 接收引脚外部中断,下降沿触发,触发后即进入起始位判断.
* @Input : None.
* @Output : None.
* @Return : None.
* @Author&Data : MrShuCai 2019.4.11.
*******************************************************************************/
void EXTI4_15_IRQHandler(void)
{
if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_4) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4); if(gsUARTBuff.UartStat == COM_NONE_BIT_DEAL)
{
gsUARTBuff.RxEn = ;
ucRecvData = ;
gsUARTBuff.UartStat = COM_START_BIT_DEAL; TIM6->ARR = TimeSet[TimeRecvStartStep1];
TIM6->EGR = TIM_EGR_UG; //初始化定时器
EXTI->IMR &= ~(0x10);
EXTI->EMR &= ~(0x10);
HAL_TIM_Base_Start_IT(&htim6);
}
} } /*******************************************************************************
* @FunctionName : BitValueChk.
* @Description : 判断采集bit值,三次中为1的次数大于等于2则值为1否则为0.
* @Input : n 采集记录的位值.
* @Output : BitValue.
* @Return : BitValue.
* @Author&Data : MrShuCai 2019.5.1.
*******************************************************************************/
uint8_t BitValueChk(uint8_t n)
{
uint8_t BitValCx = ; for(BitValCx = ; n; n >>= )
{
BitValCx += n & 0x01;
} return (BitValCx < ) ? () : (); } /*******************************************************************************
* @FunctionName : TIM6_IRQHandler.
* @Description : 中断处理函数,包括发送和接收两部分.
* @Input : None.
* @Output : None.
* @Return : None.
* @Author&Data : MrShuCai 2019.4.11.
*******************************************************************************/
void TIM6_IRQHandler(void)
{ HAL_TIM_IRQHandler(&htim6); if(gsUARTBuff.TxEn == ) /*数据发送,发送优先,无发送后才进入接收状态*/
{
switch(gsUARTBuff.UartStat) /*串口发送位状态判断*/
{
case COM_START_BIT_DEAL :
{
HAL_GPIO_WritePin(COM_TX_GPIO_Port, COM_TX_Pin, GPIO_PIN_RESET);
gsUARTBuff.UartStat = COM_DATA_BIT_DEAL;
ucSendBitCx = ;
}
break; case COM_DATA_BIT_DEAL :
{
HAL_GPIO_WritePin(COM_TX_GPIO_Port, COM_TX_Pin, (GPIO_PinState)((gsUARTBuff.UART_Send_buf[ucSendLengCx] >> ucSendBitCx) & 0x01)); ucSendBitCx ++; if(ucSendBitCx >= )
{
if(gsUARTBuff.CheckType == NONE)
{
gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
}
else
{
gsUARTBuff.UartStat = COM_CHECK_BIT_DEAL;
}
} }
break; case COM_CHECK_BIT_DEAL :
{ }
break; case COM_STOP_BIT_DEAL :
{
HAL_GPIO_WritePin(COM_TX_GPIO_Port, COM_TX_Pin, GPIO_PIN_SET); ucSendBitCx = ; if(ucSendLengCx < gsUARTBuff.Sendlength - )
{
gsUARTBuff.UartStat = COM_START_BIT_DEAL;
ucSendLengCx ++;
}
else
{
ucSendLengCx = ;
gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
gsUARTBuff.TxEn = ;
gsUARTBuff.RxEn = ;
HAL_TIM_Base_Stop_IT(&htim6);
EXTI->IMR |= 0x10;
EXTI->EMR |= 0x10;
TIM6 ->CNT = ;
} }
break; default:
break; }
} if(gsUARTBuff.RxEn == )
{
switch(gsUARTBuff.UartStat)
{
case COM_START_BIT_DEAL :
{
ucRecvBitBuff = (ucRecvBitBuff << ) | (HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin) & 0x01); if(++ ucAcquCx >= )
{
if(BitValueChk(ucRecvBitBuff) == )
{
gsUARTBuff.UartStat = COM_DATA_BIT_DEAL;
TIM6->ARR = TimeSet[ucAcquCx];
}
else
{
gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
} ucRecvBitBuff = ;
ucAcquCx = ;
}
else
{
TIM6->ARR = TimeSet[ucAcquCx];
} }
break; case COM_DATA_BIT_DEAL : //数据位
{ ucRecvBitBuff = (ucRecvBitBuff << ) | (HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin) & 0x01); if(++ ucAcquCx >= )
{
ucRecvData |= (BitValueChk(ucRecvBitBuff) & 0x01) << ucRecvBitCx; if(ucRecvBitCx >= )
{
ucRecvBitCx = ; if(gsUARTBuff.CheckType == NONE)
{
gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
}
else
{
gsUARTBuff.UartStat = COM_CHECK_BIT_DEAL;
}
}
else
{
ucRecvBitCx ++;
} TIM6->ARR = TimeSet[ucAcquCx]; ucAcquCx = ;
ucRecvBitBuff = ;
}
else
{
TIM6->ARR = TimeSet[ucAcquCx];
}
}
break; case COM_CHECK_BIT_DEAL : //校验位
{ }
break; case COM_STOP_BIT_DEAL : //停止位
{ ucRecvBitBuff = (ucRecvBitBuff << ) | (HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin) & 0x01); if( ++ ucAcquCx >= )
{
if(BitValueChk(ucRecvBitBuff) == )
{
if(gsUARTBuff.Recvlength < gsUARTBuff.UartMaxLength)
{
gsUARTBuff.UART_Recv_buf[gsUARTBuff.Recvlength] = ucRecvData;
gsUARTBuff.Recvlength ++;
} gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
HAL_TIM_Base_Stop_IT(&htim6); EXTI->IMR |= 0x10;
EXTI->EMR |= 0x10;
TIM6->CNT = ;
}
else
{
ucAcquCx = ;
} ucRecvBitBuff = ;
ucAcquCx = ;
}
else
{
TIM6->ARR = TimeSet[ucAcquCx];
}
}
break; default:
break;
}
}
}

发送就以波特率时间定时发送位状态即可,实现简单。

仅供初学或者有兴趣的朋友参考,第一种方法由于后来放弃,只是大致实现,推荐第二种,欢迎大家指点。

STM32之模拟串口设计的更多相关文章

  1. stm32 普通IO口模拟串口通信

    普通IO口模拟串口通信 串口通信协议 串口传输 默认 波特率9600 1起始位 1停止位 其他0 数据位是8位(注意图上的给错了). 传输时,从起始位开始,从一个数据的低位(LSB)开始发送,如图从左 ...

  2. 基于STM32+华为云IOT设计智能称重系统

    摘要:选择部署多个重量传感器和必要的算法.通过WiFi 通信模块.GPS定位模块,采集车辆称重数据一地理位置信息,并通过网络发送至云平台,设计图形化UI界面展示称重.地图位置等重要信息,实现对称重系统 ...

  3. 通用GPIO模拟串口,提供源代码,本人经过测试OK(第一版)

    --------------------------serial.h------------------------------------------ #ifndef _SERIAL_H_ #def ...

  4. GPIO模拟串口注意是事项

    GPIO模拟串口需要注意的事项如下:(程序见我的博客第一篇) 1.由于串口是异步通信,则串口发送必须满足宽度要求. (1)假设串口的波特率是9600bps(1s传输9600个bit),则传输1bit需 ...

  5. 【科研论文】基于文件解析的飞行器模拟系统软件设计(应用W5300)

    摘要: 飞行器模拟系统是复杂飞行器研制和使用过程中的重要设备,它可以用来模拟真实飞行器的输入输出接口,产生与真实系统一致的模拟数据,从而有效避免因使用真实飞行器带来的高风险,极大提高地面测发控系统的研 ...

  6. STM32 USB虚拟串口(转)

    源:STM32 USB虚拟串口 串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出.很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发.本章节来简单概述STM32低端芯片 ...

  7. [15单片机] STC15F104W开发入门及模拟串口程序

    STC15F104W开发入门及模拟串口程序 Saturday, 31. March 2018 09:42AM - beautifulzzzz 前言 最近找到一款51内核的SOP8封装的8脚单片机STC ...

  8. 51单片机GPIO口模拟串口通信

    51单片机GPIO口模拟串口通信 标签: bytetimer终端存储 2011-08-03 11:06 6387人阅读 评论(2) 收藏 举报 本文章已收录于:   分类: 深入C语言(20) 作者同 ...

  9. stc15f104w模拟串口使用

    stc15f104w单片机体积小,全8个引脚完全够一般的控制使用,最小系统也就是个电路滤波----加上一个47uf电容和一个103电容即可,但因为其是一个5V单片机,供电需要使用5V左右电源. 该款单 ...

随机推荐

  1. 数位dp浅谈(hdu3555)

    数位dp简介: 数位dp常用于求区间内某些特殊(常关于数字各个数位上的值)数字(比如要求数字含62,49): 常用解法: 数位dp常用记忆化搜索或递推来实现: 由于记忆化搜索比较好写再加上博主比较蒟, ...

  2. php调接口批量同步本地文件到cos或者oss

    代码: <?php namespace Main\Controller; use Common\Library\Vendor\ElasticSearch; use Common\Library\ ...

  3. 【PowerOJ1754&网络流24题】负载平衡问题(费用流)

    题意: 思路: [问题分析] 转化为供求平衡问题,用最小费用最大流解决. [建模方法] 首先求出所有仓库存货量平均值,设第i个仓库的盈余量为A[i],A[i] = 第i个仓库原有存货量 - 平均存货量 ...

  4. linux/Unix下的vim/vi指令的使用方法

    概述 以下这篇文章介绍的是关于vim的使用方法,由于我本身对linux没有太多的研究,写下的这篇文章纯属是在实际中经常使用vim指令,想通过这篇文章记录下来,方便以后使用时查找方便.个人认为,对于普通 ...

  5. 170907-关于JavaWeb的题

    1. 答案是B.D Servlet 通过调用 init () 方法进行初始化. Servlet 调用 service() 方法来处理客户端的请求. Servlet 通过调用 destroy() 方法终 ...

  6. ProtocolHandler继承体系

  7. Maps.newHashMap 和 new HashMap的区别

    区别: (1)Map<String, Object> result = new HashMap<String,Object>(); 这种是java原生API写法,需要你手动加泛 ...

  8. HDU6715 算术(莫比乌斯反演)

    HDU6715 算术 莫比乌斯反演的变形. 对 \(\mu(lcm(i,j))\) 变换,易得 \(\mu(lcm(i,j)) = \mu(i)\cdot\mu(j)\cdot \mu(gcd(i,j ...

  9. 十七、jenkins运行robotframework脚本,配置自动发送邮件

    一.配置系统管理-系统设置: A.系统管理--系统设置--Jenkins Location-系统管理员邮件地址:配置管理员邮箱全称(qq,163等都可以) B.配置管理员邮箱属性: 1.输入smtp服 ...

  10. ArrayBlockingQueue 源码分析

    ArrayBlockingQueue ArrayBlockingQueue 能解决什么问题?什么时候使用 ArrayBlockingQueue? 1)ArrayBlockingQueue 是底层由数组 ...