导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页

    D1s 通过内存映射方式,直接操作GPIO寄存器,输出速度很慢,如何提高?

    MR Series
    3
    4
    1594
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • L
      LilaZhou LV 3 最后由 编辑

      D1s, tina linux 系统,通过内存映射方式,直接操作GPIO寄存器,循环写入多个GPIO端口,共计操作671232次,需要225秒时间,平均1秒2983次,速度较慢,有什么解决办法可提高速度?

      M 1 条回复 最后回复 回复 引用 分享 0
      • M
        memory LV 7 @LilaZhou 最后由 编辑

        @lilazhou
        现在是如何操作的呢?有代码吗?

        L 1 条回复 最后回复 回复 引用 分享 0
        • L
          LilaZhou LV 3 @memory 最后由 编辑

          @memory

          这是操作GPIO的方法, 主要就用gpio_set_value方法来设置GPIO端口值。

          #include <unistd.h>
          #include <stdint.h>
          #include <stdio.h>
          #include <fcntl.h>
          #include <sys/mman.h>
          
          // 一个端口的数据结构
          typedef struct
          {
              // 工作方式寄存器 / Configure Register
              uint32_t cfg[4];
              // 数据寄存器 / Data Register
              uint32_t dat;
              // Multi_Driving Register
              uint32_t drv[4];
              // Pull Register
              uint32_t pull[3];
          } port_t;
          
          #define GPIO_MAX_PORT_COUNT	7
          // 基地址
          #define BASE_ADDR 0x02000000
          // 要映射的内存大小
          #define MEM_AREA_SIZE (GPIO_MAX_PORT_COUNT * sizeof(port_t))
          
          // port_t数组指针
          static port_t *ports = 0;
          
          int gpio_init()
          {
              // 避免重复初始化
              if (ports != 0) {
                  printf("inited already\n");
                  return 1;
              }
              // 打开/dev/mem
              int fd = open("/dev/mem", O_RDWR);
              if (fd == -1) {
                  perror("open '/dev/mem' failed\n");
                  return 1;
              }
              // 获取页大小
              uint32_t page_size = sysconf(_SC_PAGESIZE);
              // 计算基址相对于页大小的余数,也就是mmap结果之上的偏移量
              uint32_t offset = BASE_ADDR % page_size;
              // 映射到物理地址
              void *map_addr = mmap(0, MEM_AREA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BASE_ADDR - offset);
              // 映射失败
              if (map_addr == MAP_FAILED) {
                  perror("mmap failed!\n");
                  close(fd);
                  return 1;
              }
              // 可以关闭/dev/mem,对映射关系没有影响
              close(fd);
              // 把数组首地址指过去,此时就指向了第一个端口的第一个寄存器
              ports = (port_t *)(map_addr + offset);
          
              return 0;
          }
          
          // 辅助函数,检查参数,如果合法就找到对应的端口
          static port_t *check_and_get_gpio(char port, int pin)
          {
              // 避免未初始化
              if (ports == 0) {
                  printf("not inited yet!\n");
                  return 0;
              }
              // 得到端口编号
              int port_index = port - 'A';
              // 检查端口编号
              if (port_index < 0 || port_index >= GPIO_MAX_PORT_COUNT) {
                  printf("no such port '%c'!\n", port);
                  return 0;
              }
              // 检查引脚编号
              if (pin < 0 || pin >= 32) {
                  printf("no such pin '%d'!\n", pin);
                  return 0;
              }
              // 返回对应编号的端口
              return ports + port_index;
          }
          
          void gpio_set_mode(char port, int pin, int mode)
          {
              // 获取端口
              port_t *pport = check_and_get_gpio(port, pin);
              if (pport == 0) {
                  return;
              }
          
              // 指向对应的cfg寄存器;因为每个cfg寄存器可以控制8个引脚,所以是第pin/8个cfg寄存器
              uint32_t *cfg = &(pport->cfg[pin / 8]);
              // 掩码的左移位数
              int shift = (pin % 8) * 4;
              // 把cfg寄存器对应的4位清空
              *cfg &= ~(((uint32_t)0x0f) << shift);
              // 把cfg寄存器对应的4位清空设置为mode的低4位
              *cfg |= ((uint32_t)(mode & 0x0f)) << shift;
          }
          
          void gpio_set_value(char port, int pin, int value)
          {
              // 获取端口
              port_t *pport = check_and_get_gpio(port, pin);
              if (pport == 0) {
                  return;
              }
          
              // 如果为1,就把那一位置位,否则清0
              if (value == 1) {
                  pport->dat |= ((uint32_t)1) << pin;
              } else {
                  pport->dat &= ~(((uint32_t)1) << pin);
              }
          }
          
          WhycanService 1 条回复 最后回复 回复 引用 分享 0
          • WhycanService
            WhycanService LV 8 @LilaZhou 最后由 编辑

            @lilazhou 考虑CPU调度问题,Kernel可能调度到其他事项了,需要高速处理建议使用RTOS等开销较小的系统

            1 条回复 最后回复 回复 引用 分享 0
            • 1 / 1
            • First post
              Last post

            Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

            行为准则 | 用户协议 | 隐私权政策