GPIO口模拟串口发送接收(基于H861)

以前常听说码农需要有严密的逻辑思维,以前不明白。没有思维框架,真的很难写代码,不能瞎蒙,等我的只是效率低下

思路

首先我们要模拟串口通信,就要了解通信的必须条件,包括数据位及其他标志位,以及他的时序,有点像模拟IIC。
在这里插入图片描述
如图是数据的格式,数据位为八位,不是七位,当然也是有数据位为七位的,在实际的传输过程中,我选择起始位+数据为+停止位即可,奇偶校验位和空闲位不要,光是知道要传的数据,对方却不知道什么时候来接收此数据,所以我们现在需要一个协议(uart串口协议)来将数据放到这个协议上,这样就可以使得通信的上位机或者下位机能够识别传输的数据了,那还需要什么呢??
我们都知道我们在使用串口软件的时候都有设置,(只是举个例子,串口软件太多了)如图:
在这里插入图片描述
波特率、数据位、校验位、停止位都是可以更改的。
波特率概念:波特率表示每秒钟传送的码元符号的个数,它是对符号传输速率的一种度量,它用单位时间内载波调制状态改变的次数来表示,1波特即指每秒传输1个符号
一般的,我们采用模拟的形式来通信一般都在9600及以下,再高的话,精度无法保证。

在此,这里采用模拟波特率为9600的来做通信(正常来说波特率越低,难度越小)

9600的意思就是每一秒钟发送9600个bit(其中包括非数据位),注意这里不是字节(就不能当作9600/8=1200byte),很多没做过这个实验的小伙伴是不清楚这个概念的。理想情况下,按照这个传输波特率,发送每一位的时间间隔为104us,
要进行通信,就有发送与接收,发送相对接收来说要简单一点。

串口发送

发送中最重要的就是时间的控制,因为是微妙级的,需要很精确,我采用的是定时器计时。

定时器初始化:这里我们直接使用API即可(其中的hi_u32就是一个32位的数据定义),如果需要适配自己的IC,按照创建定时器创建即可

void io_hrtimer_init(hi_u32 *g_hrtimer)
{
    hi_u32 ret;
    /* create timer handle */
    ret = hi_hrtimer_create(g_hrtimer);//创建定时器
    if (ret != HI_ERR_SUCCESS)
    {
        printf("=====ERROR===== hrtimer handle create ret is: %d !!\r\n", ret);
        return;
    }
    printf("----- hrtimer handle create success -----\n");
}

GPIO初始化:每一个API都有注释说明

void io_uart_tx_init(void)
{
    hi_u32 ret;
    ret = hi_gpio_init(); //GPIO初始化
    if (ret != HI_ERR_SUCCESS)
    {
        printf("===== ERROR ===== gpio -> hi_gpio_init ret:%d\r\n", ret);
        return;
    }
    printf("----- gpio init success-----\r\n");

    ret = hi_io_set_func(HI_IO_NAME_GPIO_14, HI_IO_FUNC_GPIO_14_GPIO);//开启GPIO复用
    if (ret != HI_ERR_SUCCESS)
    {
        printf("===== ERROR ===== gpio -> hi_io_set_func ret:%d\r\n", ret);
        return;
    }
    printf("----- io set func success-----\r\n");

    ret = hi_gpio_set_dir(HI_GPIO_IDX_14, HI_GPIO_DIR_OUT);//设置管脚方向
    if (ret != HI_ERR_SUCCESS)
    {
        printf("===== ERROR ===== gpio -> hi_gpio_set_dir1 ret:%d\r\n", ret);
        return;
    }
    printf("----- gpio set dir success! -----\r\n");
    hi_gpio_set_ouput_val(HI_GPIO_IDX_14, HI_GPIO_VALUE1); //初始化输出高电平
}

定时器延时函数及回调中断

static hi_void hrtimer_callback(hi_u32 data)
{
    block_or_go_on = 0;
}
void hr_delay(hi_u32 time)
    int ret;
    ret = hi_hrtimer_start(rx_timer, time, hrtimer_callback, go_on);
    if (ret != 0)
    {
        printf("-----hi timer faild-----\r\n");
    }
    printf("block_or_go_on:%d\r\n", block_or_go_on);
    while (block_or_go_on);
    block_or_go_on = 1;
}

停止函数

void stop_function(void)
{
    hi_hrtimer_start(g_hrtim, (time_), hrtimer_callback, go_on);//开启定时器API
    io_uart_tx(1);
    while (!block_or_go_on);
    block_or_go_on = 1;
}

开始函数

void start_function(void)
{
    hi_hrtimer_start(g_hrtim, (time_), hrtimer_callback, go_on);//开启定时器API	
    io_uart_tx(0);
    while (!block_or_go_on);
    block_or_go_on = 1;
}

主函数:

#define time_ (100)
#define block 0
#define go_on 1
 main()
{
#define time_ (100)
static volatile char block_or_go_on = 1;
int ret = 0;
    int i = 0;
    char data;
    hi_u32 g_hrtim;
    char b[] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
    io_uart_tx_init();
    io_hrtimer_init(&g_hrtim);
    for (;;)
    {
printf("---has enter TX----\r\n");
        for (ret = 0; ret < 100; ret++)//发送100个数据,查看是否有误
        {
            data = b[ret];
            start_function();
            for (i = 0; i < 8; i++)
            {
                hi_gpio_set_ouput_val(HI_GPIO_IDX_14, data & 0x01);//发送数据API
                hr_delay(time_ - 5);
                data >>= 1;
            }
            stop_function();
        }
        hr_delay(2000000);
    }
    }

这里要说明的是,我将开始位和一定的延时组合到一起,停止位亦如此,数据位后加延时,共同组成整个时序。写好之后数据一般都不对,我们可以用串口软件接收,看收到的数据,因为程序运行需要时间,所以一般都不是104us,我调出来是100us,还得根据自身的情况更改,代码框架是ok的。

串口接收(依旧9600)

思路:开启定时中断,进入中断后关闭中断,在中断函数内进行高低电平判断,将这些bit存储最后转换成需要的数据,也就是说,一个中断函数,完成一个字节的接收!
定时器初始化及中断

static hi_void hrtimer_callback(hi_u32 data)
{
    block_or_go_on = 0;
}
void io_hrtimer_init(hi_u32 *g_hrtimer)
{
    hi_u32 ret;
    /* create timer handle */
    ret = hi_hrtimer_create(g_hrtimer);
    if (ret != HI_ERR_SUCCESS)
    {
        printf("=====ERROR===== hrtimer handle create ret is: %d !!\r\n", ret);
        return;
    }
    printf("----- hrtimer handle create success -----\n");
}

GPIO中断初始化及中断函数:看注释

static volatile hi_u32 irq_flag=0;
static void irq_function(hi_void *arg)
{
    hi_gpio_deinit();
    irq_flag = 1;
    printf("-----has enter irq_function-----\r\n");
}
void gpio_rx_irq_init(void)
{
    hi_u32 ret;
    ret = hi_gpio_init(); //注意,该接口不支持重复调用,只能在初始化阶段调用一次
    //irq_flag = ret;
    if (ret != HI_ERR_SUCCESS)
    {
        printf("===== ERROR ===== gpio -> hi_gpio_init ret:%d\r\n", ret);
        return;
    }
    printf("----- gpio init success-----\r\n");

    ret = hi_gpio_set_dir(HI_GPIO_IDX_5, HI_GPIO_DIR_IN);//方向
    if (ret != HI_ERR_SUCCESS)
    {
        printf("===== ERROR ===== gpio -> hi_gpio_set_dir ret:%d\r\n", ret);
        return;
    }
    printf("----- dir init success-----\r\n");

    ret = hi_io_set_func(HI_GPIO_IDX_5, HI_IO_FUNC_GPIO_5_GPIO);//复用
    if (ret != HI_ERR_SUCCESS)
    {
        printf("===== ERROR ===== gpio -> hi_io_set_func ret:%d\r\n", ret);
        return;
    }
    printf("----- io set func success-----\r\n");
    ret = hi_gpio_register_isr_function(HI_GPIO_IDX_5, HI_INT_TYPE_EDGE, HI_GPIO_EDGE_FALL_LEVEL_LOW, irq_function, 0); //开启中断功能
    if (ret != HI_ERR_SUCCESS)
    {
        printf("===== ERROR ===== gpio -> hi_gpio_register_isr_function ret:%d\r\n", ret);
        return;
    }
    printf("----- func init success-----\r\n");
}

主函数

#define _time_ (100)
#define BIT(x) (0x01<<(x))
#define block 0
#define go_on 1
main()
{
    hi_u32 rx_timer;
    io_hrtimer_init(&rx_timer); //定时器初始化
    gpio_rx_irq_init();
    for (;;)
    {
        if (irq_flag == 1)
        {
            int ret = 0;
            int electric_level[3] = {0};
            int bit_value;
            char data = 0;
            for (ret = 0; ret < 10; ret++)//数据位+停止位+开始位=10
            {
                hi_hrtimer_start(rx_timer, (_time_ - 8), hrtimer_callback, go_on);//进行了时间数据的微调
                while (block_or_go_on);
                block_or_go_on = 1;
                hi_gpio_get_input_val(HI_GPIO_IDX_5, &bit_value);
                if (ret == 8) //第九位停止位为1
                {
                    printf("4\r\n");

                    break;
                }
                if (bit_value == 1)
                {
                    data |= BIT(ret);
                    printf("1\r\n");
                }
                else
                {
                    printf("0\r\n");
                }
                block_or_go_on = 1;//置1
            }
            printf("%02X ", data);
            irq_flag = 0;//中断标志位置1
            hi_gpio_init();//开启中断
        }
    }
}

这里我只是给出一个代码框架,细节问题还需要自己去调整,不能做到都copy就ok,为什么这次不在中断里做处理,因为我用的这个IC的GPIO和定时器中断的优先级是相同的,且无法修改。
以上的正确接收的time时间我是试的,就只会在104的周围,还看你的代码的简化程度!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值