美文网首页
GPIO 模拟Uart 通信 (soft uart/serial

GPIO 模拟Uart 通信 (soft uart/serial

作者: Nothing_655f | 来源:发表于2020-12-19 14:28 被阅读0次

GPIO 模拟Uart 通信 (soft uart/serial)

在Uart不够用的时候可以通过GPIO 来模拟,但是GPIO 模拟有一个缺点就是时钟可能不准,Uart是异步的,我们可以设置两个定时器来模拟其对应的输出。

流程

linux下的GPIO模拟Uart涉及到如下几个内容

1、GPIO初始化、设定输入输出、以及输入中断设置

2、初始化定时器,建议是高精度定时器

3、中断处理数据接收处理和发送数据

4、fifo管理数据

5、注册tty 驱动管理

代码移植

我这边主要是移植了树莓派的soft_uart 驱动,驱动代码路径 soft_uart/soft_serial

找了好久的资料和网址才找到适用的,感谢github!!!求点赞收藏~~

我这边修改为platform_dirver 设备驱动模型了, 这个取决于你的使用场景

我这边改用platform_dirver的原因有如下几个

1、DTS可以动态配置中断号,GPIO管脚

2、通过 struct platform_device *pdev 可以获取更多数据结构来操作

3、本人使用的arm 平台申请IRQ方法跟github 对应的gpio irq 方法不一样,需要依赖指定的中断号

Module.c

static const struct of_device_id soft_uart_of_match[] = {
    {.compatible = "soft_uart",},
};

static struct platform_driver soft_uart = {
    .driver = {
        .name   = "soft_uart",
        .owner = THIS_MODULE,
        .of_match_table = soft_uart_of_match,
    },
    .probe = soft_uart_probe,
    .remove = soft_uart_remove,
};

static int __init soft_uart_init(void)
{
    return platform_driver_register(&soft_uart);
}

static void __exit soft_uart_exit(void)
{
    platform_driver_unregister(&soft_uart);
}

获取指定的GPIO

#include <linux/gpio/consumer.h>
struct gpio_desc {
    struct gpio_chip    *chip;
    unsigned long       flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED  0
#define FLAG_IS_OUT 1
#define FLAG_EXPORT 2   /* protected by sysfs_lock */
#define FLAG_SYSFS  3   /* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL  4   /* trigger on falling edge */
#define FLAG_TRIG_RISE  5   /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 6   /* value has active low */
#define FLAG_OPEN_DRAIN 7   /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8  /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9  /* GPIO is connected to an IRQ */

#define ID_SHIFT    16  /* add new flags before this one */

#define GPIO_FLAGS_MASK     ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK   (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))

#ifdef CONFIG_DEBUG_FS
    const char      *label;
#endif
};

static int soft_uart_probe(struct platform_device *pdev)
{
  struct gpio_desc *dt_gpio_tx, *dt_gpio_rx;
  struct device *dev = &pdev->dev;

  printk(KERN_INFO "soft_uart: Initializing module...\n");

  dt_gpio_tx = gpiod_get(dev, "gpio_tx");
  if(IS_ERR(dt_gpio_tx)) {
      pr_err("gpio_tx gpiod_get failed\n");
      dt_gpio_tx = NULL;
  } else {
      set_bit(FLAG_OPEN_DRAIN, &dt_gpio_tx->flags);
      gpio_tx = desc_to_gpio(dt_gpio_tx);
  }

  dt_gpio_rx = gpiod_get(dev, "gpio_rx");
  if(IS_ERR(dt_gpio_rx)) {
      pr_err("gpio_rx gpiod_get failed\n");
      dt_gpio_rx = NULL;
  } else {
      set_bit(FLAG_OPEN_DRAIN, &dt_gpio_rx->flags);
      gpio_rx = desc_to_gpio(dt_gpio_rx);
  }
  // ........
 }

Dts 配置

    soft_uart{
        compatible = "soft_uart";
        dev_name = "soft_uart";
        status = "okay";
        interrupts = <0 67 1>;
        gpio_tx-gpios = <&gpio GPIOH_3 GPIO_ACTIVE_HIGH>;
        gpio_rx-gpios = <&gpio GPIOH_2 GPIO_ACTIVE_HIGH>;
    };

调试问题

修复好编译问题后就是一些移植调试问题

记录一下这个过程中遇到的一些问题吧

1、probe中的 gpiod_get 调用后, 调用了 raspberry_soft_uart_init 中的 gpio_request 会返回 -16 (EBUSY)导致驱动挂载不上,看了下gpiolib中的源码后发现 gpiod_get 其实是有调用了 gpio_request ,两者取一就好了

2、另外硬件选择的这组GPIO是OD的,一开始并不清楚,还在疑惑为什么Tx端的IO老是拉不高,如果是OD,需要外接上拉,目前硬件是已经外接上拉了,关于更多一些OD的介绍,可以看这篇文章 https://blog.csdn.net/qq_43033547/article/details/88759002

不过代码中需要配置IO的属性为OD的属性,即前面init中配置的

set_bit(FLAG_OPEN_DRAIN, &dt_gpio_tx->flags);

简单介绍下设置成OD Flag 后一个电压设置流程

static void _gpiod_set_raw_value(struct gpio_desc *desc, int value)
{
        struct gpio_chip        *chip;

        chip = desc->chip;
        trace_gpio_value(desc_to_gpio(desc), 0, value);
        if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
                _gpio_set_open_drain_value(desc, value);
        else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
                _gpio_set_open_source_value(desc, value);
        else
                chip->set(chip, gpio_chip_hwgpio(desc), value);
}

/*
 *  _gpio_set_open_drain_value() - Set the open drain gpio's value.
 * @desc: gpio descriptor whose state need to be set.
 * @value: Non-zero for setting it HIGH otherise it will set to LOW.
 */
 static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value)
{
        int err = 0;
        struct gpio_chip *chip = desc->chip;
        int offset = gpio_chip_hwgpio(desc);

        //pr_info("%s %d\n", __func__, __LINE__);
        if (value) {
                err = chip->direction_input(chip, offset);
                if (!err)
                        clear_bit(FLAG_IS_OUT, &desc->flags);
        } else {
                err = chip->direction_output(chip, offset, 0);
                if (!err)
                        set_bit(FLAG_IS_OUT, &desc->flags);
        }
        trace_gpio_direction(desc_to_gpio(desc), value, err);
        if (err < 0)
                gpiod_err(desc,
                          "%s: Error in set_value for open drain err %d\n",
                          __func__, err);
}

可以看到OD flag 是输出高的情况是设置为了输入模式,这是为什么???

结合图来看


1608206854412.png

设置为输出,芯片管脚下拉为低,所以电压为低

设置为输入,芯片管脚为阻态,由于有外部上拉,所以电压为高

3、使用的是时候需要配置波特率,最好是配置为4800bps

stty -F /dev/ttySOFT0 speed 4800

参照ReadMe

Usage

The device will appear as /dev/ttySOFT0. Use it as any usual TTY device.

You must be included in the group dialout. You can verify in what groups you are included by typing groups. To add an user to the group dialout, type:

sudo usermod -aG dialout <username>

Usage examples:

minicom -b 4800 -D /dev/ttySOFT0
cat /dev/ttySOFT0
echo "hello" > /dev/ttySOFT0

Baud rate

When choosing the baud rate, take into account that:

  • The Raspberry Pi is not very fast.
  • You will probably not be running a real-time operating system.
  • There will be other processes competing for CPU time.

As a result, you can expect communication errors when using fast baud rates. So I would not try to go any faster than 4800 bps.

相关文章

  • GPIO 模拟Uart 通信 (soft uart/serial

    GPIO 模拟Uart 通信 (soft uart/serial) 在Uart不够用的时候可以通过GPIO 来模拟...

  • stm32 gpio模拟uart通信

    记 最近在做TI cc2640的项目,需要用gpio模拟串口通信, 没有头绪,第一次做关于外设的开发,以前一直做上...

  • 12/19

    stm32f407串口通信: void UART1_config(void) { GPIO_InitTypeDef...

  • MT2503 串口编程

    配置GPIO模式为UART 首先是配置IO为复用UART功能,使用 或 初始化串口 配置IO为UART后才能初始化...

  • 2017-12-12

    今天我们黄老师为我们讲解了UART 通用异步串行通信,UART--通用异步串行通信接口的总称,UART允许在串行链...

  • 2017 .12.12

    今天上午学习了Uart串行异步通信 UART.any() # 返回缓冲区中接收的字符数 UART.read([nb...

  • 酷比魔方 I7 手写板驱动程序

    主板: Intel(R) Serial IO UART Host Controller - 9CE4_1.1.25...

  • UART通信

    串口(USART)通信--串口通讯协议简介 物理层与协议层 一、物理层 1.RS232标准 ...

  • UART通信

    配置流程 1.使能时钟,函数RCC_APB1PeriphClockCmd(); 2.初始化,函数GPIO_Init...

  • 树莓派4b串口(UART)使用问题的解决方法 - Ubuntu

    硬件连接 Pin map 官方设计时都是将“硬件串口”分配给GPIO中的UART(GPIO14&GPIO15),因...

网友评论

      本文标题:GPIO 模拟Uart 通信 (soft uart/serial

      本文链接:https://www.haomeiwen.com/subject/miftnktx.html