STM32之模拟串口设计

一、设计用途:

公司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 = 0;         //每次接收的字节
uint8_t ucAcquCx = 0;           //三次滤波计数
uint8_t ucRecvBitCx = 0;        //接收位计数
uint8_t ucSendBitCx = 0;        //发送位计数
uint8_t ucSendLengCx = 0;       //发送长度计数

uint8_t ucRecvLastBit = 0;     //上次接收位记录
uint8_t ucRecvNowBit = 0;      //当前位记录
uint8_t ucRecvTrueCx = 0;     //正确计数

uint32_t Prescaler = 0;
uint32_t Period = 0;
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, 0, 0);
    HAL_NVIC_EnableIRQ(TIM6_IRQn);

    HAL_TIM_Base_Init(&htim6);

}

void UART_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __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, 0, 0);
    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 == 1200)
    {
        Prescaler = 15;
        Period = 623;
    }
    else if(BaudRate == 2400)
    {
        Prescaler = 15;
        Period = 207;
    }
    else if(BaudRate == 4800)
    {
//        Prescaler = 15;
//        Period = 50;  //9600
             Prescaler = 15;
             Period = 103;
    }

    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 = 1;
    gsUARTBuff.RxEn = 0;
    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 = 1;
            ucRecvData = 0;
            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 == 1)       /*数据发送,发送优先,无发送后才进入接收状态*/
    {
        switch(gsUARTBuff.UartStat)  /*串口发送位状态判断*/
        {
        case COM_START_BIT_DEAL :
        {
            HAL_GPIO_WritePin(COM_TX_GPIO_Port, COM_TX_Pin, GPIO_PIN_RESET);

            if(++ ucAcquCx == 6)    /*由于发送时在立即拉低就进入判断,且自加,所以需要加到4,实际延时3个周期*/
            {
                gsUARTBuff.UartStat = COM_DATA_BIT_DEAL;
                ucAcquCx = 0;
                ucSendBitCx = 0;
            }
        }
        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 >= 6)
            {
                ucAcquCx = 0;

                ucSendBitCx ++;

                if(ucSendBitCx >= 8)
                {
                    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 == 7)
            {
                ucAcquCx = 0;
                ucSendBitCx = 0;

                if(ucSendLengCx < gsUARTBuff.Sendlength - 1)
                {
                    gsUARTBuff.UartStat = COM_START_BIT_DEAL;
                    ucSendLengCx ++;
                }
                else
                {
                    ucSendLengCx = 0;
                    gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
                    gsUARTBuff.TxEn = 0;
                    gsUARTBuff.RxEn = 1;

                    HAL_TIM_Base_Stop_IT(&htim6);
                    TIM6->CNT = 0;
                }
            }
        }
        break;

        default:
            break;

        }
    }

    if(gsUARTBuff.RxEn == 1)
    {
        switch(gsUARTBuff.UartStat)
        {
        case COM_START_BIT_DEAL :                         //起始位 必须连续三次采集正确才认可
        {
            ucRecvLastBit = ucRecvNowBit;
            ucRecvNowBit = HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin);

            if(    ucAcquCx == 0)
            {
                ucAcquCx = 1;
                return ;
            }

            if(ucRecvLastBit == ucRecvNowBit)
            {
                ucRecvTrueCx ++;
            }

            if(++ ucAcquCx >= 5)
            {
                if(ucRecvTrueCx >= 2)
                {
                    gsUARTBuff.UartStat = COM_DATA_BIT_DEAL;
                    ucAcquCx = 0;
                    ucRecvTrueCx = 0;
                }
                else
                {
                    ucAcquCx = 0;
                    ucRecvTrueCx = 0;
                    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(0 == ucAcquCx)
            {
                ucAcquCx = 1;
                return;
            }

            if(ucRecvNowBit == ucRecvLastBit)
            {
                ucRecvTrueCx ++;
            }

            if(++ ucAcquCx >= 6)
            {
                ucAcquCx = 0;

                if(ucRecvTrueCx >= 2)
                {
                    if(ucRecvLastBit == 1)
                    {
                        ucRecvData |= (1 << ucRecvBitCx);
                    }
                    else
                    {
                        ucRecvData &= ~(1 << ucRecvBitCx);
                    }

                    if(ucRecvBitCx >= 7)
                    {
                        ucRecvBitCx = 0;

                        if(gsUARTBuff.CheckType == NONE)
                        {
                            gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
                        }
                        else
                        {
                            gsUARTBuff.UartStat = COM_CHECK_BIT_DEAL;
                        }
                    }
                    else
                    {
                        ucRecvBitCx ++;
                    }
                }
                else
                {
                    ucAcquCx = 0;
                    ucRecvTrueCx = 0;
                    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(0 == ucAcquCx)
            {
                ucAcquCx = 1;
                return;
            }

            if(ucRecvNowBit == ucRecvLastBit && ucRecvNowBit == 1)
            {
                ucRecvTrueCx ++;
            }

            if( ++ ucAcquCx >= 6)
            {
                ucAcquCx = 0;

                if(ucRecvTrueCx >= 2)
                {
                                      ucRecvTrueCx = 0;
                    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 = 0;
                }
                else
                {
                    ucRecvTrueCx = 0;
                }
            }
        }
        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 = 0,
    TimeRecvStep2 = 1,
    TimeRecvStep3 = 2,
    TimeRecvStep1 = 3,
    TimeSendStep = 4,

} TimeStep;

uint16_t TimeSet[5];

uint16_t TimeSetBuff[][5] = {{1199, 59, 59, 2378, 2498 }, //1200
    {539, 59, 59, 1128, 1247 },  //2400
    {239, 59, 59, 503, 624  },   //4800
    {149, 29, 29, 251, 311  },   //9600
    {0, 0, 0, 0, 0    },         //预留
};

UART gsUARTBuff;                //定义串口

uint8_t ucRecvData = 0;         //每次接收的字节
uint8_t ucAcquCx = 0;           //三次滤波计数
uint8_t ucRecvBitCx = 0;        //接收位计数
uint8_t ucSendBitCx = 0;        //发送位计数
uint8_t ucSendLengCx = 0;       //发送长度计数

uint8_t ucRecvBitBuff = 0;       //采集位保存

TIM_HandleTypeDef htim6;

void MX_TIM6_Init(void)
{

    __HAL_RCC_TIM6_CLK_ENABLE();

    htim6.Instance = TIM6;
    htim6.Init.Prescaler = 15;
    htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim6.Init.Period = TimeSet[TimeRecvStartStep1];
    htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

    HAL_NVIC_SetPriority(TIM6_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM6_IRQn);

    HAL_TIM_Base_Init(&htim6);

}

void UART_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __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, 0, 0);
    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 == 1200)
    {
        memcpy(TimeSet, &TimeSetBuff[0][0], sizeof(TimeSet));
    }
    else if(BaudRate == 2400)
    {
        memcpy(TimeSet, &TimeSetBuff[1][0], sizeof(TimeSet));
    }
    else if(BaudRate == 4800)
    {
        memcpy(TimeSet, &TimeSetBuff[2][0], sizeof(TimeSet));
    }
    else if(BaudRate == 9600)
    {
        memcpy(TimeSet, &TimeSetBuff[3][0], 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 = 1;
            gsUARTBuff.RxEn = 0;
            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 = 1;
            ucRecvData = 0;
            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 = 0;

    for(BitValCx = 0; n; n >>= 1)
    {
        BitValCx += n & 0x01;
    }

    return (BitValCx < 2) ? (0) : (1);

}

/*******************************************************************************
* @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 == 1)         /*数据发送,发送优先,无发送后才进入接收状态*/
    {
        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 = 0;
        }
        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 >= 8)
            {
                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 = 0;

            if(ucSendLengCx < gsUARTBuff.Sendlength - 1)
            {
                gsUARTBuff.UartStat = COM_START_BIT_DEAL;
                ucSendLengCx ++;
            }
            else
            {
                ucSendLengCx = 0;
                gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
                gsUARTBuff.TxEn = 0;
                gsUARTBuff.RxEn = 1;
                HAL_TIM_Base_Stop_IT(&htim6);
                              EXTI->IMR |= 0x10;
                          EXTI->EMR |= 0x10;
                TIM6 ->CNT = 0;
            }

        }
        break;

        default:
            break;

        }
    }

    if(gsUARTBuff.RxEn == 1)
    {
        switch(gsUARTBuff.UartStat)
        {
                    case COM_START_BIT_DEAL :
                    {
                            ucRecvBitBuff = (ucRecvBitBuff << 1) | (HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin) & 0x01);

                            if(++ ucAcquCx >= 3)
                            {
                                    if(BitValueChk(ucRecvBitBuff) == 0)
                                    {
                                            gsUARTBuff.UartStat = COM_DATA_BIT_DEAL;
                                            TIM6->ARR = TimeSet[ucAcquCx];
                                    }
                                    else
                                    {
                                            gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
                                    }

                                    ucRecvBitBuff = 0;
                                    ucAcquCx = 0;
                            }
                            else
                            {
                                    TIM6->ARR = TimeSet[ucAcquCx];
                            }

                    }
                    break;

                    case COM_DATA_BIT_DEAL :                         //数据位
                    {

                            ucRecvBitBuff = (ucRecvBitBuff << 1) | (HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin) & 0x01);

                            if(++ ucAcquCx >= 3)
                            {
                                    ucRecvData |= (BitValueChk(ucRecvBitBuff) & 0x01) << ucRecvBitCx;

                                    if(ucRecvBitCx >= 7)
                                    {
                                            ucRecvBitCx = 0;

                                            if(gsUARTBuff.CheckType == NONE)
                                            {
                                                    gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
                                            }
                                            else
                                            {
                                                    gsUARTBuff.UartStat = COM_CHECK_BIT_DEAL;
                                            }
                                    }
                                    else
                                    {
                                            ucRecvBitCx ++;
                                    }

                                    TIM6->ARR = TimeSet[ucAcquCx];

                                    ucAcquCx = 0;
                                    ucRecvBitBuff = 0;
                            }
                            else
                            {
                                    TIM6->ARR = TimeSet[ucAcquCx];
                            }
                    }
                    break;

                    case COM_CHECK_BIT_DEAL :                         //校验位
                    {

                    }
                    break;

                    case COM_STOP_BIT_DEAL :                         //停止位
                    {

                        ucRecvBitBuff = (ucRecvBitBuff << 1) | (HAL_GPIO_ReadPin(COM_RX_GPIO_Port, COM_RX_Pin) & 0x01);

                            if( ++ ucAcquCx >= 3)
                            {
                                    if(BitValueChk(ucRecvBitBuff) == 1)
                                    {
                                            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 = 0;
                                    }
                                    else
                                    {
                                            ucAcquCx = 0;
                                    }

                                    ucRecvBitBuff = 0;
                                    ucAcquCx = 0;
                            }
                            else
                            {
                                    TIM6->ARR = TimeSet[ucAcquCx];
                            }
                    }
                    break;

                    default:
                            break;
                    }
    }
}

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

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

原文地址:https://www.cnblogs.com/wangshucai/p/10817204.html

时间: 2024-10-09 21:56:53

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

stm32 普通IO口模拟串口通信

普通IO口模拟串口通信 串口通信协议 串口传输 默认 波特率9600 1起始位 1停止位 其他0 数据位是8位(注意图上的给错了). 传输时,从起始位开始,从一个数据的低位(LSB)开始发送,如图从左向右的顺序,对电平拉高或拉低,最后停止位时拉高. 波特率大小,改变延时时间即可.例如9600 波特率    根据公式 : 1/9600=0.000104s(大致) 也就是说每发送1bit延时104us (下面我用9600波特率来说,代码用的是19200) 串口发送       将电平拉低 延时104

用IO模拟串口协议发送数据

<pre name="code" class="cpp">//文件usend.h #ifndef _USEND_H_ #define _USEND_H_ //====红外接收相关定义============================= #define PuTx_High (P_uTx = 1) //数据高 #define PuTx_Low (P_uTx = 0) //数据低 #define V_SendDatNum 6//6 //发送数据字节数 /

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

51单片机GPIO口模拟串口通信 标签: bytetimer终端存储 2011-08-03 11:06 6387人阅读 评论(2) 收藏 举报 本文章已收录于: 分类: 深入C语言(20) 作者同类文章X 1 #include "reg52.h" 2 #include "intrins.h" 3 #include "math.h" 4 #include "stdio.h" 5 sbit BT_SND =P1^5; 6 sbit

[计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(一)

一.首先说明: 这是啥? —— 这是利用C#FORM写的一个用来演示计算机图形学中 ①Bresenham直线扫描算法(即:连点成线):②种子填充法(即:填充多边形):③扫描线填充法 有啥用? ——  无论是连点成线还是区域填充在高级编程中基本上都提供很高效的库函数来调用.这里拿出这些算法一方面有利于大家理解那些封装的函数底层是实现:另一方面是方便嵌入式TFT屏幕底层驱动开发时借鉴的. 是啥样? ——  如下面的操作,不言而喻. 二.进入正题: 2-1.直线的扫描转换 图形的扫描转换实质就是在光栅

模拟串口输出

最近在调试hardfault handler输出日志信息时候获得了Vincent帮助,感谢 1 void Delay_UART5(uint16_t cnt) 2 { 3 for(;cnt;cnt--); 4 } 1 //void HardFault_Handler(void) //uart5 38400 2 //{ 3 // 4 // uint32_t addrbegin=__get_MSP() ; //???·?· 5 uint32_t addr: 6 // uint8_t i, j, str

STM32 USB虚拟串口(转)

源:STM32 USB虚拟串口 串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出.很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发.本章节来简单概述STM32低端芯片上的USB虚拟串口的移植.在官方DEMO中已经提供了现成的程序,这里对修改方法做简单说明. 官方demo及驱动程序,我存放在百度盘: http://pan.baidu.com/s/1hq3moE4 首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下: Projects\Virt

模拟串口--基于STM8普通IO口的模拟串口驱动程序

基于STM8普通IO口的模拟串口驱动程序 标准串口通讯数据的格式为:起始位(1) + 数据位(8) + 校验位(1) + 停止位(1) 串口通讯另外一个重要的的部分是设置波特率,波特率就是1秒钟内串口所传输的Bit(位)数. 关于采样频率:为了较小读取或者发送串行数据的误差,我们采取了在N(我用的是4次)次中断中,取固定位置的读取的数据. 我以stm8中9600波特率计算的过程为例:(1秒钟传输9600位) 可以计算出传输1位所需要的时间 T1 = 1/9600 约为104us 由此可知,发送一

stc15f104w模拟串口使用

stc15f104w单片机体积小,全8个引脚完全够一般的控制使用,最小系统也就是个电路滤波----加上一个47uf电容和一个103电容即可,但因为其是一个5V单片机,供电需要使用5V左右电源. 该款单片机视乎没有硬件串口,所以想要使用串口完成开发则应该使用软件模拟串口实现,其中P3.0为单片机RX,P3.1为单片机TX,下载时也适用.最好的学习质料莫过于官网,使用可以链接:http://www.stcmcudata.com/STC-LIB/STC15%E7%B3%BB%E5%88%97%E5%B

教你如何在51单片机上模拟串口通信!!!

我们可以不使用单片机本身带有的串口,而自己用程序去模拟一个串口并达到和本身的串口具有同样的功能, 首先,我们需要用到CH340串口模块,大家可以上某宝自行购买. 正面: 反面: 然后我们需要了解一下这串口模块上的引脚: 5V  :与VCC短路为5V TLL输出(电源和信号输出都是5V) VCC:可以与3.3V和5V用跳帽连接 3.3V:与VCC短路为3.3V TLL输出(电源和信号输出都是3.3V) TXD:发送数据端口(与单片机上的接收引脚用杜邦线连接) RXD:接收数据端口(与单片机上的发送