普通IO口模拟串口-滤波实现

原创文章

原创作者文章

修改内容

1、功能定义模块化,提高移植性了,白嫖党福音,拿来就是直接用!!!
2、因为资源不够用将定时器修改为系统定时器
3、修改采样位置,这个看实际情况调整的
4、增加接收超时处理
5、增加接收时间采样补偿,这个看实际情况调整的
6、因为利用定时器进行收发,在发送时需要停止接收事件

SIMU_UART.c 串口初始化

函数外部接口指针,串口参数初始化

#include "SIMU_UART.h"

#if(SIMU_UART_SRC == SIMU_METHOD1)
#include "simu_uart1.h"
#elif(SIMU_UART_SRC == SIMU_METHOD2)
#include "simu_uart2.h"
#elif(SIMU_UART_SRC == SIMU_METHOD3)
// #include "simu_uart3.h"
#endif

SIMU_UART simu_uart = {0};
volatile UART gsUARTBuff = {0}; // 定义串口
UART_KEY_MSG desk_key_msg = {0};

void simu_uart_init(void)
{
    simu_uart.baudrate = BAUDRATE;
    simu_uart.data_bits = 8;
    simu_uart.stop_bits = 1; // 这个默认
    simu_uart.parity = 0;

#if(SIMU_UART_SRC == SIMU_METHOD1)
    
    simu_uart.ext_irq = UART_EXT_IRQ_METHOD1;
    simu_uart.timer_irq = UART_TIM_IRQ_METHOD1;
    simu_uart.write = UART_Write_Data_API_METHOD1;
    simu_uart.read = UART_Read_Data_API_METHOD1;
    UART_Init_METHOD1(simu_uart);
#elif(SIMU_UART_SRC == SIMU_METHOD2)

    simu_uart.ext_irq = UART_EXT_IRQ_METHOD2;
    simu_uart.timer_irq = UART_TIM_IRQ_METHOD2;
    simu_uart.write = UART_Write_Data_API_METHOD2;
    simu_uart.read = UART_Read_Data_API_METHOD2;
    UART_Init_METHOD2(simu_uart);
#elif(SIMU_UART_SRC == SIMU_METHOD3)

#endif
    
}

void over_time_open_rx(void)
{
    // 当处于工作状态超过1000ms就超时恢复接收状态
    if(gsUARTBuff.UartStat != COM_NONE_BIT_DEAL && (++gsUARTBuff.over_rx_time > 1000))
    {
        gsUARTBuff.over_rx_time = 0;
        gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
        gsUARTBuff.RxEn = 0;
        gsUARTBuff.TxEn = 0;
#if(SIMU_UART_SRC == SIMU_METHOD1)
        UART_TIM_ENABLE_METHOD1(0, 0);
        UART_EXT_ENABLE_METHOD1(1);
#elif(SIMU_UART_SRC == SIMU_METHOD2)
        UART_TIM_ENABLE_METHOD2(0, 0);
        UART_EXT_ENABLE_METHOD2(1);
#endif
    }
}
// 测试
void simu_uart_test(void)
{
    uint8_t databuff[40] = {0}, len = 0;
    static uint16_t delay_time = 0;

    if(++delay_time > 2000)
    {
        delay_time = 0;

        len = simu_uart.read(databuff, 40);
        
        if(len)
        {
            simu_uart.write(databuff, len);
        }
    }
}

void uart_data_process(uint8_t *data, uint16_t size)
{
    uint8_t cmd_sta = CMD_FRAME_STA, len = 0, j = 0, data_buff[7] = {0};
    uint16_t check_sum = 0;

    for(uint16_t i = 0; i < size; i++)
    {
        switch (cmd_sta)
        {
        case CMD_FRAME_STA:
            if(data[i] == 0x55)
                cmd_sta = CMD_LEN_STA;
            /* code */
            break;
        case CMD_LEN_STA:
            len = data[i];
            check_sum = 0;

            cmd_sta = CMD_FRAME_STA; // 如果校验和不过就重新开始
            if(i + len < size - 1)
            {
                for(j = i; j <= i + len; j++)
                {
                    check_sum += data[j];
                }

                if(check_sum & 0xff == data[j])
                    cmd_sta = CMD_DATA_STA;
            }
            break;
        case CMD_DATA_STA:
            memcpy(data_buff, &data[i], len);
            i += len - 1;
            cmd_sta = COM_CHECK_BIT_DEAL;
            break;
        case COM_CHECK_BIT_DEAL:
            cmd_sta = CMD_FRAME_STA;
            break;    
        default:
            break;
        }
    }

    memcpy(&desk_key_msg, data_buff, 7);
}

SIMU_UART.h

#ifndef _SIMU_UART_H_
#define _SIMU_UART_H_

#include "stdint.h"
#include "AppInclude.h"

#define     SIMU_METHOD1    0
#define     SIMU_METHOD2    1    
#define     SIMU_METHOD3    2
#define     SIMU_UART_SRC     SIMU_METHOD1

#define SYSTICK_TIM_ENABLE  1
#define TIM_SRC_FREQ    96 * 1000000  // 时钟频率,单位:Hz
#define TIM_PRESCALER   1 // 默认1
#define BAUDRATE        9600
#define DATA_BUFF_LEN   100

#define TX_IO(x)      if(x){SetPB6();}else{ResetPB6();}

#define RX_IO       ReadPB0()

#define TEST_IO(x)     if(x){SetPC5();}else{ResetPC5();}

#define TEST_IO_DELAY(x)   {TEST_IO(1);DelayUs(x);TEST_IO(0);DelayUs(x);}
        
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define MIN(x,y) ((x) > (y) ? (y) : (x))
#define COMPARE(x, y) ((x) == (y) ? 1 : 0)      
        
typedef struct
{
    uint16_t baudrate;
    uint8_t data_bits;
    uint8_t stop_bits;
    uint8_t parity;

    void (*timer_irq)(void);
    void (*ext_irq)(void);
    uint8_t (*write)(uint8_t *data, uint16_t size);
    uint16_t (*read)(uint8_t *data, uint16_t size);
}SIMU_UART;

typedef enum
{
    COM_NONE_BIT_DEAL = 0,
    COM_START_BIT_DEAL,
    COM_DATA_BIT_DEAL,
    COM_CHECK_BIT_DEAL,
    COM_STOP_BIT_DEAL,
}UART_RX_STA;

typedef enum
{
    // CMD_NONE_STA = 0,
    CMD_FRAME_STA = 0,
    CMD_LEN_STA,
    CMD_DATA_STA,
    CMD_CHECK_STA,
}UART_PRO_STA;

typedef struct
{
    uint8_t CheckType;
    uint8_t DataBits;

    uint8_t UartStat;
    uint8_t TxEn;
    uint8_t RxEn;

    uint8_t UART_Send_buf[DATA_BUFF_LEN];
    uint8_t UART_Recv_buf[DATA_BUFF_LEN];

    uint16_t Sendlength;
    uint16_t Recvlength;

    float BaudTime;

    uint16_t curren_time;
    uint16_t over_rx_time;
}UART;

typedef struct
{
    uint8_t height;
    uint8_t Key_1_sta;
    uint8_t Key_2_sta;
    uint8_t Key_3_sta;
    uint8_t Key_s_sta;
    uint8_t Key_up_sta;
    uint8_t Key_down_sta;
}UART_KEY_MSG;

void simu_uart_init(void);
void simu_uart_test(void);
void over_time_open_rx(void);
void uart_data_process(uint8_t *data, uint16_t size);

extern SIMU_UART simu_uart;

extern volatile UART gsUARTBuff;

extern UART_KEY_MSG desk_key_msg;
#endif

simu_uart1.c 模拟串口实现

一些io初始化

/*
// 使用通用定时器和外部中断:外部中断触发启动接收,三次接收滤波。定时器负责接收、发送。
// 定时时钟源48M,分频后定时器3M
*/

#include "simu_uart1.h"
// 定时器加载值
static uint32_t TimeSet[5];

// 时间,第一个是调节开始采集数据的位置
float TimeSetBuff[][5] = {
    {250, 20, 20, 793.33, 833.33}, // 1200
    {100, 20, 20, 376.67, 416.67},  // 2400
    {30, 20, 20, 168.33, 208.33},    // 4800
    {5, 20, 20, 64.17, 104.17},    // 9600
    {0, 0, 0, 0, 0},            // 预留
};

// 时间
// float TimeSetBuff[][5] = {
//     {400, 20, 20, 793.33, 833.33}, // 1200
//     {180, 20, 20, 376.67, 416.67},  // 2400
//     {80, 20, 20, 168.33, 208.33},    // 4800
//     {50, 10, 10, 84.17, 104.17},    // 9600
//     {0, 0, 0, 0, 0},            // 预留
// };

static uint8_t ucRecvData = 0;    // 每次接收的字节
static uint8_t ucAcquCx = 0;      // 三次滤波计数
static uint8_t ucRecvBitCx = 0;   // 接收位计数
static uint8_t ucSendBitCx = 0;   // 发送位计数
static uint8_t ucSendLengCx = 0;  // 发送长度计数
static uint8_t ucRecvBitBuff = 0; // 采集位保存
static uint8_t time_offset = 0;

#if (!SYSTICK_TIM_ENABLE)
TIM_HandleTypeDef htim6;
#endif
/*******************************************************************************
 * @FunctionName   : UART_TIM_Init_METHOD1.
 * @Description    : 定时器初始化.
 * @Input          : 定时器时间.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_TIM_Init_METHOD1(uint32_t time)
{
#if (SYSTICK_TIM_ENABLE)
#if (TIM_PRESCALER > 1)
    /*
    code systick分频代码
    */
#endif

    // 初始化为波特率时间:us
    if (time <= 0xFFFFFFUL)
    {
        SysTick->LOAD = (uint32_t)time; /* set reload register */

        NVIC_SetPriority(SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */

        SysTick->VAL = 0UL; /* Load the SysTick Counter Value  | (1UL << 0)*/
        SysTick->CTRL = (1UL << 2) | (1UL << 1);
        
    }
#else
    __HAL_RCC_TIM6_CLK_ENABLE();

    htim6.Instance = TIM6;
    htim6.Init.Prescaler = 15;
    htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim6.Init.Period = time;
    htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

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

    HAL_TIM_Base_Init(&htim6);
#endif
}

/*******************************************************************************
 * @FunctionName   : UART_GPIO_Init_METHOD1.
 * @Description    : 串口模拟io口初始化.
 * @Input          : None.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_GPIO_Init_METHOD1(void)
{
    SetInputModePB0();
    SetPullUpPB0();

    SetOutputModePB6();
    SetPullUpPB6();

    SetOutputModePC5();
    SetPullUpPC5();

    rSYSTEM1.per1.csbscl_gpio_sel = 0;

}
/*******************************************************************************
 * @FunctionName   : UART_Rx_Ext_Init_METHOD1.
 * @Description    : 串口模拟io口所用中断初始化.
 * @Input          : None.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_Rx_Ext_Init_METHOD1(void)
{
	rSYSTEM2.per0.ext_intr2_level_edge_sel = 1;		// 选择边沿触发
	rSYSTEM2.per0.ext_intr2_rise_falling_sel = 1;	// 下降沿触发
	
	rINTR.sts.ext_intr2 = 1;						// 请中断
	rINTR.mask.ext_intr2 = 1;						// 开中断

	NVIC_SetPriority(ExtIntr2_IRQ,1);
	NVIC_ClearPendingIRQ(ExtIntr2_IRQ);
	NVIC_EnableIRQ(ExtIntr2_IRQ);
}
/*******************************************************************************
 * @FunctionName   : UART_Init_METHOD1.
 * @Description    : 模拟串口结构体初始化.
 * @Input          : 串口参数结构体.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_Init_METHOD1(SIMU_UART simu_uart)
{
    float temp_buff[5] = {0};

    gsUARTBuff.CheckType = simu_uart.parity;
    gsUARTBuff.DataBits = simu_uart.data_bits;

    gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;

    if (simu_uart.baudrate == 1200)
    {
        memcpy(temp_buff, &TimeSetBuff[0][0], sizeof(temp_buff));
    }
    else if (simu_uart.baudrate == 2400)
    {
        memcpy(temp_buff, &TimeSetBuff[1][0], sizeof(temp_buff));
    }
    else if (simu_uart.baudrate == 4800)
    {
        memcpy(temp_buff, &TimeSetBuff[2][0], sizeof(temp_buff));
    }
    else if (simu_uart.baudrate == 9600)
    {
        memcpy(temp_buff, &TimeSetBuff[3][0], sizeof(temp_buff));
    }

    for (uint8_t i = 0; i < 5; i++)
    {
        TimeSet[i] = (uint32_t)(TIM_SRC_FREQ / TIM_PRESCALER / 1000000.0 * temp_buff[i] + 0.5 - 1);
    }

    time_offset = (uint8_t)(TIM_SRC_FREQ / TIM_PRESCALER / 1000000.0);

    UART_GPIO_Init_METHOD1();

    UART_Rx_Ext_Init_METHOD1();

    UART_TIM_Init_METHOD1(TimeSet[TimeSendStep]);

}

/*******************************************************************************
 * @FunctionName   : UART_EXT_IRQ_METHOD1.
 * @Description    : 接收引脚外部中断,下降沿触发,触发后即进入起始位判断.
 * @Input          : None.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_EXT_IRQ_METHOD1(void)
{
    /*
    // 清除中断标志等操作
    // if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_4) != RESET)
    // {
    //     __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
    //     flag = 1;
    // }
    */

    static uint8_t flag = 1;
    
    if (gsUARTBuff.UartStat == COM_NONE_BIT_DEAL)
    {
        gsUARTBuff.over_rx_time = 0;

        gsUARTBuff.RxEn = 1;
        ucRecvData = 0;
        ucRecvBitCx = 0;
        gsUARTBuff.UartStat = COM_START_BIT_DEAL;
        // buff溢出进行重新初始化索引
        if(gsUARTBuff.Recvlength >= DATA_BUFF_LEN)
            gsUARTBuff.Recvlength = 0;

        UART_EXT_ENABLE_METHOD1(0); // 接收先屏蔽事件

        UART_TIM_ENABLE_METHOD1(1, TimeSet[TimeRecvStartStep1]);
    }
}

/*******************************************************************************
 * @FunctionName   : UART_TIM_IRQ_METHOD1.
 * @Description    : 中断处理函数,包括发送和接收两部分.
 * @Input          : None.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_TIM_IRQ_METHOD1(void)
{
    /*
    // 清除中断标志等操作
    // HAL_TIM_IRQHandler(&htim6); // This function handles TIM interrupts requests.
    */

    UART_Send_Data_METHOD1();

    UART_Recv_Data_METHOD1();

}

/*******************************************************************************
 * @FunctionName   : UART_Send_Data_METHOD1.
 * @Description    : 发送数据.
 * @Input          : None.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_Send_Data_METHOD1(void)
{
    if (gsUARTBuff.TxEn == 1) /*数据发送,发送优先,无发送后才进入接收状态*/
    {
        switch (gsUARTBuff.UartStat) /*串口发送位状态判断*/
        {
        // 启动位
        case COM_START_BIT_DEAL:
        {
            TX_IO(0);                               // 拉低初始化io
            gsUARTBuff.UartStat = COM_DATA_BIT_DEAL; // 进入下个阶段
            ucSendBitCx = 0;                         // 初始化发送记数
        }
        break;
        // 数据位
        case COM_DATA_BIT_DEAL:
        {
            TX_IO((gsUARTBuff.UART_Send_buf[ucSendLengCx] >> ucSendBitCx) & 0x01);

            ucSendBitCx++;

            if (ucSendBitCx >= gsUARTBuff.DataBits)
            {
                if (gsUARTBuff.CheckType == 0)
                {
                    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:
        {
            TX_IO(1); // 拉高停止

            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;

                UART_TIM_ENABLE_METHOD1(0, 0);
                UART_EXT_ENABLE_METHOD1(1);
            }
        }
        break;

        default:
            break;
        }
    }
}

/*******************************************************************************
 * @FunctionName   : UART_Send_Data_METHOD1.
 * @Description    : 接收数据.
 * @Input          : None.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_Recv_Data_METHOD1(void)
{
    if (gsUARTBuff.RxEn == 1)
    {
        
        switch (gsUARTBuff.UartStat)
        {
        case COM_START_BIT_DEAL:
        {
            
            // 接收保存
            ucRecvBitBuff = (ucRecvBitBuff << 1) | (RX_IO & 0x01);

            if (++ucAcquCx >= 3)
            {   
                // 启动位-拉低,否则无效
                if (BitValueChk(ucRecvBitBuff) == 0)
                {
                    gsUARTBuff.UartStat = COM_DATA_BIT_DEAL;
                    UART_TIM_CTRL_METHOD1(TimeSet[ucAcquCx]);
                }
                else
                {
                    gsUARTBuff.UartStat = COM_STOP_BIT_DEAL;
                }

                ucRecvBitBuff = 0;
                ucAcquCx = 0;
            }
            else
            {
                UART_TIM_CTRL_METHOD1(TimeSet[ucAcquCx]); // 修改重载值
            }
        }
        break;

        case COM_DATA_BIT_DEAL: // 数据位
        {

            ucRecvBitBuff = (ucRecvBitBuff << 1) | (RX_IO & 0x01);

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

                if (ucRecvBitCx >= gsUARTBuff.DataBits - 1)
                {
                    ucRecvBitCx = 0;

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

                UART_TIM_CTRL_METHOD1(TimeSet[ucAcquCx]);
                ucAcquCx = 0;
                ucRecvBitBuff = 0;
            }
            else
            {
                UART_TIM_CTRL_METHOD1(TimeSet[ucAcquCx]);
            }
        }
        break;

        case COM_CHECK_BIT_DEAL: // 校验位
        {
        }
        break;

        case COM_STOP_BIT_DEAL: // 停止位
        {

            ucRecvBitBuff = (ucRecvBitBuff << 1) | (RX_IO & 0x01);

            if (++ucAcquCx >= 3)
            {   
                // 空闲-拉高,否则重新记数获取停止位
                if (BitValueChk(ucRecvBitBuff) == 1)
                {
                    if (gsUARTBuff.Recvlength < DATA_BUFF_LEN)
                    {
                        gsUARTBuff.UART_Recv_buf[gsUARTBuff.Recvlength] = ucRecvData;
                        gsUARTBuff.Recvlength++;

                        
                    }

                    gsUARTBuff.UartStat = COM_NONE_BIT_DEAL;
                    gsUARTBuff.RxEn = 0;
                    UART_TIM_ENABLE_METHOD1(0, 0);
                    UART_EXT_ENABLE_METHOD1(1);
                }
                else
                {
                    ucAcquCx = 0;
                }

                ucRecvBitBuff = 0;
                ucAcquCx = 0;
            }
            else
            {
                UART_TIM_CTRL_METHOD1(TimeSet[ucAcquCx]);
            }
        }
        break;

        default:
            break;
        }
    }
}

/*******************************************************************************
 * @FunctionName   : UART_TIM_ENABLE_METHOD1.
 * @Description    : 定时器使用前后使能.
 * @Input          : 是否使能,初始化时间(适用使能).
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_TIM_ENABLE_METHOD1(uint8_t flag, uint32_t time)
{
#if (SYSTICK_TIM_ENABLE)
    if (flag)
    {
        if(SysTick->CTRL & 0x01)
            SysTick->CTRL &= ~(1UL << 0);

        SysTick->LOAD = (uint32_t)time;
        SysTick->VAL = 0UL;
        SysTick->CTRL |= (1UL << 0);
    }
    else
    {
        SysTick->CTRL &= ~(1UL << 0);
        SysTick->VAL = 0UL;
    }
#else
    if (flag)
    {
        TIM6->ARR = time;
        TIM6->EGR = TIM_EGR_UG;

        HAL_TIM_Base_Start_IT(&htim6);
    }
    else
    {
        HAL_TIM_Base_Stop_IT(&htim6);
        TIM6->CNT = 0;
    }
#endif
}

/*******************************************************************************
 * @FunctionName   : UART_TIM_CTRL_METHOD1.
 * @Description    : 定时器重加载.
 * @Input          : 初始化时间.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_TIM_CTRL_METHOD1(uint32_t time)
{
    time = TimeSet_Offset(time);

#if (SYSTICK_TIM_ENABLE)
    // if(SysTick->CTRL & 0x01)
    //     SysTick->CTRL &= ~(1UL << 0);

    SysTick->LOAD = (uint32_t)time;

    // SysTick->CTRL |= (1UL << 0);
    // UART_TIM_ENABLE_METHOD1(1, time); // 这里有些奇异,如果重启会不准,和清空记数有关
#else
    TIM6->ARR = time;
#endif
}

/*******************************************************************************
 * @FunctionName   : UART_EXT_ENABLE_METHOD1.
 * @Description    : 定时器使用前后使能.
 * @Input          : 是否使能.
 * @Output         : None.
 * @Return         : None.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
void UART_EXT_ENABLE_METHOD1(uint8_t flag)
{
    if (flag)
    {
        rINTR.sts.ext_intr2 = 1;						// 请中断
        rINTR.mask.ext_intr2 = 1;						// 开中断

        // EXTI->IMR |= 0x10;
        // EXTI->EMR |= 0x10;
    }
    else
    {
        rINTR.sts.ext_intr2 = 1;						// 请中断
        rINTR.mask.ext_intr2 = 0;						// 关中断

        // EXTI->IMR &= ~(0x10); // 中断屏蔽
        // EXTI->EMR &= ~(0x10); // 事件屏蔽
    }
}

/*******************************************************************************
 * @FunctionName   : UART_Write_Data_API_METHOD1.
 * @Description    : 模拟串口发送数据接口.
 * @Input          : 数据,数据长度.
 * @Output         : None.
 * @Return         : 发送状态.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
uint8_t UART_Write_Data_API_METHOD1(uint8_t *data, uint16_t size)
{
    if (gsUARTBuff.UartStat == COM_NONE_BIT_DEAL)
    {
        gsUARTBuff.over_rx_time = 0;
        // memcpy(gsUARTBuff.UART_Send_buf, data, size);

        if(DATA_BUFF_LEN < size)
            size = DATA_BUFF_LEN;

        gsUARTBuff.Sendlength = size;
        for(uint16_t i = 0; i < size; i++)
        {
            gsUARTBuff.UART_Send_buf[i] = data[i];
        }
        
        ucSendLengCx = 0;
        gsUARTBuff.TxEn = 1;
        gsUARTBuff.RxEn = 0;
        gsUARTBuff.UartStat = COM_START_BIT_DEAL;
        // 发送前一定要屏蔽接收中断,要不然发送时基会被打乱
        UART_EXT_ENABLE_METHOD1(0);
        UART_TIM_ENABLE_METHOD1(1, TimeSet[TimeSendStep]);

        return 1;
    }

    return 0;
}

/*******************************************************************************
 * @FunctionName   : UART_Read_Data_API_METHOD1.
 * @Description    : 模拟串口读取数据接口初始化.
 * @Input          : 数据.
 * @Output         : 接收数据.
 * @Return         : 数据长度.
 * @Author&Data    : MrShuCai  2019.4.11.
 *******************************************************************************/
uint16_t UART_Read_Data_API_METHOD1(uint8_t *data, uint16_t size)
{
    if (gsUARTBuff.UartStat == COM_NONE_BIT_DEAL && gsUARTBuff.Recvlength)
    {
        gsUARTBuff.over_rx_time = 0;
        // memcpy(data, gsUARTBuff.UART_Recv_buf, gsUARTBuff.Recvlength);

        if(gsUARTBuff.Recvlength < size)
            size = gsUARTBuff.Recvlength;

        for(uint16_t i = 0; i < size; i++)
        {
            data[i] = gsUARTBuff.UART_Recv_buf[i];
        }

        gsUARTBuff.Recvlength = 0;

        return size;
    }
    return 0;
}

/*******************************************************************************
 * @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);
}


uint32_t TimeSet_Offset(uint32_t time)
{
#if(BAUDRATE == 9600)
    if(ucRecvBitCx > 6)
    {
        if(ucAcquCx == 1 || ucAcquCx == 2)
        {
            time -= time_offset * (ucRecvBitCx - 1);
        }
        else if(ucAcquCx == 3)
        {
            time += time_offset * (ucRecvBitCx - 1) * 2;
        }
    }
#endif

    return time;
    
}


simu_uart1.h

#ifndef _SIMU_UART1_H_
#define _SIMU_UART1_H_

#include "stdint.h"
#include "string.h"
#include "SIMU_UART.h"

typedef enum
{
    TimeRecvStartStep1 = 0,
    TimeRecvStep1 = 1,
    TimeRecvStep2 = 2,
    TimeRecvStep3 = 3,
    TimeSendStep = 4,

}TimeStep;

void UART_EXT_ENABLE_METHOD1(uint8_t flag); // 外部中断事件使能

void UART_TIM_ENABLE_METHOD1(uint8_t flag, uint32_t time); // 定时器使用前后使能

void UART_TIM_CTRL_METHOD1(uint32_t time);

void UART_TIM_IRQ_METHOD1(void); // 定时器中断

void UART_EXT_IRQ_METHOD1(void); // 外部中断

void UART_GPIO_Init_METHOD1(void); // 串口模拟io口初始化

void UART_TIM_Init_METHOD1(uint32_t time); // 定时器配置

void UART_Rx_Ext_Init_METHOD1(void); // 串口模拟io口所用中断初始化

void UART_Init_METHOD1(SIMU_UART simu_uart); // 串口初始化

void UART_Send_Data_METHOD1(void); // 发送数据

void UART_Recv_Data_METHOD1(void); // 接收数据

uint8_t BitValueChk(uint8_t n); // 滤波
uint32_t TimeSet_Offset(uint32_t time);

uint8_t UART_Write_Data_API_METHOD1(uint8_t *data, uint16_t size); // 发送数据
uint16_t UART_Read_Data_API_METHOD1(uint8_t *data, uint16_t size); // 接收数据

#endif

说明

此文章主要用于学习记录,如有违规引用,请联系博主删除。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
外部中断模拟串口,波特率不能超过65536 实验测试:发送57600可以正常,但接收只能 <= 38400 #include "Uart_EXT0.h" #include "MAIN.h" #define FOCS 22114800ul bit Over; bit bRxflag; unsigned char IEN0_NOW,IEN1_NOW; //中断临时变量 unsigned char idata bRxstate=0; //接收状态 unsigned char idata tmp_Len=0; //缓存数组下标 unsigned char idata bRxlen; //接收字节数 unsigned char xdata EX_buf[64]; //接收存放区 /*************************************************** baud = 56000 接收一字节 =178.6us ,接收会出现错误 baud = 38400 接收一字节 =260.4us //快速接收都容易出现错误 baud = 19200 接收一字节 =520us | baud = 14400 接收一字节 =694.5us | baud = 9600 接收一字节 =1041.6us | baud = 4800 接收一字节 =2.083ms 接收过长,定时器MODE2无法满足 //主机发送的数据最好加上校验字 发送:最大57600,发送还能正常。 ****************************************************/ void Time0_Uart(unsigned int baud) { TMOD &= 0XF0; TMOD |= 0X02; //使用定时器0方式2,使用方式1则2400波特也能有 TH0 = 256-(FOCS/12)/baud; TL0 = TH0; IP0 |= 0x02; //设置为最高优先级 IP1 |= 0x02; TR0 = 1; ET0 = 1; EA = 1; } void Time0_isr() interrupt 1 { //方式1则重装值 Over = 1; } /* 发送数据,1起始位,8数据,1停止位 发送波特率实验57600都不会错误,大量发送数据待测试 */ void Uart_set(unsigned char dat) { unsigned char i; Over = 0; TL0 = TH0; //防止发送数据开始时不知道TL0是多少 T_uart = 0; //起始位 while(Over==0); Over = 0; for(i=0;i>= 1; } // Over = 0; //数据位清零了 T_uart = 1; //停止位,此后如果没有数据则一直为高 while(Over==0); } unsigned char get_Uart() { unsigned char I=0,get_dat=0; Over = 0; TL0 = TH0; //重新赋值,防止出错 while(Over==0); Over = 0; for(I=0;I>= 1; if(Get_ex == 1) get_dat |= 0x80; else get_dat &= 0x7f; while(Over==0); Over = 0; } if(Get_ex == 1) { Over = 0; } return get_dat; } void EXuart_isr() interrupt 0 { unsigned char _chr; IEN0_NOW = IEN0; //进入接收1字节,关闭中断 IEN1_NOW = IEN1; IEN0 = 0X82; IEN1 = 0; _chr=get_Uart(); switch(bRxstate) { case 0: if(_chr==0x55) { bRxstate=1; } else bRxstate=0; break; case 1: if(_chr==0xaa) { bRxstate=2; } else bRxstate=0; break; case 2: //取得数据长度 bRxlen=_chr; bRxstate=3; break; case 3: EX_buf[tmp_Len]=_chr; tmp_Len++; if(tmp_Len==bRxlen) { bRxstate=0; tmp_Len=0; bRxflag=1; //一帧数据接收完毕,置位完成标志位 } break; default: break; } IE0 = 0; //清外部0中断标志,防止下次接收不到 IEN0 = IEN0_NOW; IEN1 = IEN1_NOW; } void main() { Time0_Uart(9600); EX_uart(); if(bRxflag==1) { bRxflag = 0; for(i=0;i<10;i++) RevBuffer[i] = EX_buf[i]; T0Uart_TX(10,RevBuffer); } }
您好!感谢您的提问。以下是使用MPU6050卡尔曼滤波实现6轴模拟鼠标的C#代码示例: ``` using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO.Ports; public class MPU6050Mouse : MonoBehaviour { SerialPort sp = new SerialPort("COM3", 9600); //定义串口 float[] angle = new float[3]; float[] gyro = new float[3]; float[] accel = new float[3]; float[] gyro_offset = new float[3]; float[] accel_offset = new float[3]; float[] angle_speed = new float[3]; float[] angle_last = new float[3]; float[] angle_now = new float[3]; float[] angle_acc = new float[3]; float[] angle_offset = new float[3]; float[] Q = new float[3] { 0.001f, 0.001f, 0.001f }; float[] R = new float[3] { 0.03f, 0.03f, 0.03f }; float[] P = new float[3] { 0f, 0f, 0f }; float[] K = new float[3] { 0f, 0f, 0f }; float dt; float roll, pitch, yaw; float mouse_speed_x, mouse_speed_y; // Start is called before the first frame update void Start() { sp.Open(); //打开串口 sp.ReadTimeout = 20; //设置读取超时时间 gyro_offset[0] = 0; gyro_offset[1] = 0; gyro_offset[2] = 0; accel_offset[0] = 0; accel_offset[1] = 0; accel_offset[2] = 0; angle_last[0] = 0; angle_last[1] = 0; angle_last[2] = 0; } // Update is called once per frame void Update() { string str = sp.ReadLine(); //读取串口数据 string[] data = str.Split(','); //分割数据 gyro[0] = float.Parse(data[0]); gyro[1] = float.Parse(data[1]); gyro[2] = float.Parse(data[2]); accel[0] = float.Parse(data[3]); accel[1] = float.Parse(data[4]); accel[2] = float.Parse(data[5]); dt = Time.deltaTime; gyro[0] -= gyro_offset[0]; gyro[1] -= gyro_offset[1]; gyro[2] -= gyro_offset[2]; if (Mathf.Abs(accel[0]) < 0.6f && Mathf.Abs(accel[1]) < 0.6f && Mathf.Abs(accel[2]) < 0.6f) { angle_acc[0] = Mathf.Atan(accel[1] / Mathf.Sqrt(Mathf.Pow(accel[0], 2) + Mathf.Pow(accel[2], 2))) * Mathf.Rad2Deg; angle_acc[1] = Mathf.Atan(-accel[0] / Mathf.Sqrt(Mathf.Pow(accel[1], 2) + Mathf.Pow(accel[2], 2))) * Mathf.Rad2Deg; angle_acc[2] = 0; angle_speed[0] = gyro[0] - angle_offset[0]; angle_speed[1] = gyro[1] - angle_offset[1]; angle_speed[2] = gyro[2] - angle_offset[2]; for (int i = 0; i < 3; i++) { angle_now[i] = angle_last[i] + angle_speed[i] * dt; P[i] = P[i] + Q[i]; K[i] = P[i] / (P[i] + R[i]); angle_now[i] = angle_now[i] + K[i] * (angle_acc[i] - angle_now[i]); P[i] = (1 - K[i]) * P[i]; angle_last[i] = angle_now[i]; } roll = angle_now[0]; pitch = angle_now[1]; yaw = angle_now[2]; mouse_speed_x = pitch; mouse_speed_y = roll; Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; transform.Rotate(-mouse_speed_x, mouse_speed_y, 0); //旋转物体 } } void OnApplicationQuit() { sp.Close(); //关闭串口 } } ``` 以上代码实现了通过MPU6050卡尔曼滤波获取物体的旋转角度,从而模拟鼠标的移动。其中,gyro_offset和accel_offset是用来校准陀螺仪和加速度计的偏移量;Q、R、P和K是卡尔曼滤波中的参数;mouse_speed_x和mouse_speed_y是模拟鼠标的速度。希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值