导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页
    1. 主页
    2. xfdr0805
    X
    • 资料
    • 关注 0
    • 粉丝 3
    • 我的积分 1476
    • 主题 9
    • 帖子 23
    • 最佳 4
    • 群组 0

    xfdr0805LV 5

    @xfdr0805

    1476
    积分
    13
    声望
    42
    资料浏览
    23
    帖子
    3
    粉丝
    0
    关注
    注册时间 最后登录

    xfdr0805 取消关注 关注

    xfdr0805 发布的最佳帖子

    • 动手让LVGL8带的Music-Demo音乐响起来(代码已上传)
      1. LVGL8 自带的有一个音乐播放器的Demo,效果不错,充分展示了LVGL8的弹性网格布局,动画等效果,来学习GUI是非常不错的,只是这个Demo并不能真正的播放音乐,但是已经把音乐播放的逻辑都给实现了,现在我们就动手给它注入灵魂----mpv
      2. 最初是用的sox这个号称音频界的瑞士军刀的工具,但是编写过程中碰到一个比较棘手的问题,那就是无法使用管道重定向输出(play本身不支持管道,只能借助sox),也就没办法获取播放进度,会造成进度条时间与实际播放时间有点误差,最后就放弃使用sox了

      2021-09-19_11-14.png

              if (pid == 0)
              {
                  LOG_D("child pid:%d\n", getpid());
                  char cmd[32];
                  prctl(PR_SET_PDEATHSIG, SIGKILL);
                  close(0);
                  dup2(pip[1], 1); //标准输出重定向到管道输出
                  close(pip[0]);
                  sprintf(buf, "./music/%s", _lv_demo_music_get_title(track_id));
                  sprintf(cmd, "--start=%d", _time, _lv_demo_music_get_track_length(track_id));
                  // execlp("ls", "ls", "./music", NULL);
                  // execlp("play", "play", "-p", buf, "trim", cmd, NULL);
                  // execlp("sox", "sox", buf, "-p", "|", "play", "-", "trim", cmd, NULL);
                  //最后知道怎么使用管道了,但是这是2个进程
                  // sox ./music/云非非\ -\ 邂逅.flac -t flac - | play -t flac - &
                  // sox ./music/云非非\ -\ 邂逅.flac -t flac - | play - &
                  // sox ./music/云非非\ -\ 邂逅.flac -p | play - &
                  return 0;
              } 
      
      1. 下边请出在后台默默工作的大佬MPV

        MPV播放器是什么?

        MPV是著名开源播放器mplayer和mplayer2的一个分支。
        mplayer则是这个地球上最强的播放器(没有之一),跨平台的特性使得windows、mac、linux上都可以见到它的身影,电脑、手机上很多播放器也是基于它开发的,由于mplayer不带界面,所以很多时候你都不知道是它在默默为你工作。
        并且mplayer播放视频时对于资源的消耗往往最少,所以你会发现在一台配置极差的电脑上播放高清电影,mplayer通常是最流畅的,使用快进时最能体现出差距,其他播放器已经画面卡死时,mplayer的画面可能只是感觉到掉帧而已。
        MPV播放器继承这些众多优良特性的同时,添加了内置于窗口的播放控制界面(OSC),对硬解的良好支持,以及其他额外特性。由于口碑很好,使得著名的mplayer前端:smplayer在不久前也添加了对MPV的支持,现在的smplayer你可以在mplayer和MPV2个核心之间切换使用。
        mpv官网的开发文档比较好,参考使用手册都可以使用起来

      2. 下边简单说一下过程
        a.首先要获取播放列表 播放列表里的字体是浪漫雅圆,使用freetype渲染的 。获取列表有2种方法
          方法1:
        
          int scan_music_list(char *_path)
          {
              char path[128];
              sprintf(path, "ls %s", _path);
              FILE *fp = popen(path, "r");
              if (ferror(fp))
              {
                  LOG_D("error\n");
              }
              char buf[128];
              while (!feof(fp))
              {
                  fgets(buf, sizeof(buf), fp);
                  strncpy(title_list[idx], buf, strlen(buf) - 1); //去掉文件名后的\n
                  get_music_info(title_list[idx], idx);
                  idx++;
                  usleep(1);
              }
              return idx;
          } 
          
        
         方法2:
        
         int list_dir(char *path, int depth)
         {
             DIR *dir;
             struct dirent *file;
             struct stat st;
             dir = opendir(path);
             if (!dir)
             {
                 LOG_D("open dir %s failed!", path);
                 return -1;
             }
             LOG_D("open dir %s ok!", path);
             while ((file = readdir(dir)) != NULL)
             {
                 if (strncmp(file->d_name, ".", 1) == 0 || strncmp(file->d_name, "..", 2) == 0)
                 {
                     continue;
                 }
                 strcpy(title_list[idx++], file->d_name);
                 if (stat(file->d_name, &st) >= 0 && S_ISDIR(st.st_mode) && depth <= 5)
                 {
                     list_dir(file->d_name, depth + 1);
                 }
             }
             closedir(dir);
             return 0;
         }      ```
        
        b.程序启动时创建一个子进程,一个线程获取播放进度,一个线程获取各种输入
          void lv_demo_music(void)
          {
              lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x343247), 0);
        
              music_num = scan_music_list(MUSIC_PATH);
              list = _lv_demo_music_list_create(lv_scr_act());
              ctrl = _lv_demo_music_main_create(lv_scr_act());
              pid = vfork();
              if (pid == 0)
              {
                  LOG_D("child pid:%d\n", getpid());
                  prctl(PR_SET_PDEATHSIG, SIGKILL);
                  execlp("mpv", "mpv", "--quiet", "--no-terminal", "--no-video", "--idle=yes", "--term-status-msg=", "--input-ipc-server=/tmp/mpvsocket", NULL);
                  LOG_D("child exit!\n");
                  return 0;
              }
              else if (pid > 0)
              {
                  LOG_D("parent pid:%d\n", getpid());
                  sleep(1);
                  close(0);
                  act.sa_handler = sigusr1;
                  sigfillset(&act.sa_mask);
                  act.sa_flags = SA_RESTART; /* don't fiddle with EINTR */
                  sigaction(SIGUSR1, &act, NULL);
                  addr.sun_family = AF_UNIX;
                  strcpy(addr.sun_path, "/tmp/mpvsocket");
                  fd_mpv = socket(AF_UNIX, SOCK_STREAM, 0);
                  if (fd_mpv == -1)
                  {
                      LOG_D("Create socket failed\n");
                  }
                  if (connect(fd_mpv, (struct sockaddr *)&addr, sizeof(addr)) == -1)
                  {
                      LOG_D("Cannot connect to socket %s\n", addr.sun_path);
                  }
                  if (pthread_create(&mthread, NULL, get_music_percent_pos, NULL) != 0)
                  {
                      LOG_D("pthread create error!\n");
                      return 0;
                  }
                  // LOG_D("get music pos pthread create ok!\n");
              }
              else
              {
                  LOG_D("fork error:\n");
              }
        
          #if LV_DEMO_MUSIC_AUTO_PLAY
              lv_timer_create(auto_step_cb, 1000, NULL);
          #endif
          }
        
          这里说明一下mpv里的参数
          "--quiet", //输出尽量少的信息
          "--no-terminal", //不接受终端输入
          "--no-video", //不需要视频
          "--idle=yes", //播放完不能出进程
          "--term-status-msg=", //状态信息不打印
          "--input-ipc-server=/tmp/mpvsocket"//使用sock方式与mpv通信
          这里使用的是mpv推荐的基于socket的JSON-based IPC protocol通信方式
        
        c. 一个线程启动时向mpv发送命令,然后监听事件就可以知道当前进度及状态
          void *get_music_percent_pos(void *arg)
          {
              // char cmd[] = "{\"command\": [ \"get_property\", \"playback-time\"] }\n";
              // char cmd[] = "{\"command\": [ \"get_property\", \"percent-pos\"] ,\"request_id\":2}\n";
              char cmd2[] = "{\"command\": [ \"observe_property\",2, \"percent-pos\"]}\n";
              char cmd1[] = "{\"command\": [  \"observe_property\",1,\"time-pos\"]}\n";
              char buf[512];
              cJSON *root;
              cJSON *event;
              cJSON *percent;
              write(fd_mpv, cmd1, strlen(cmd1));
              _msleep(100);
              write(fd_mpv, cmd2, strlen(cmd2));
              memset(buf, 0, sizeof(buf));
              while (1)
              {
        
                  if (read(fd_mpv, buf, sizeof(buf)) > 0)
                  {
                      //LOG_D("--->:%s\n", buf);
                      root = cJSON_Parse(buf);
                      if (root == NULL)
                      {
                          LOG_D("cJSON parse error!\n");
                          return;
                      }
                      // LOG_D("cJSON parse ok!\n");
                      if (cJSON_HasObjectItem(root, "event"))
                      {
        
                          event = cJSON_GetObjectItem(root, "event");
                          if (event != NULL)
                          {
                              if (strcmp(event->valuestring, "start-file") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "metadata-update") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "file-loaded") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "property-change") == 0 && (cJSON_GetObjectItem(root, "id")->valueint == 1))
                              {
                                  percent = cJSON_GetObjectItem(root, "data");
                                  lv_label_set_text_fmt(time_obj, "%02d:%02d", (int)percent->valuedouble / 60, (int)percent->valuedouble % 60);
                                  LOG_D("time pos:%d\n", (int)percent->valuedouble);
                              }
                              else if (strcmp(event->valuestring, "property-change") == 0 && (cJSON_GetObjectItem(root, "id")->valueint == 2))
                              {
                                  percent = cJSON_GetObjectItem(root, "data");
                                  lv_slider_set_value(slider_obj, (int)percent->valuedouble, LV_ANIM_ON);
                                  LOG_D("percent pos:%d\n", (int)percent->valuedouble);
                              }
                              else if (strcmp(event->valuestring, "end-file") == 0)
                              {
                                  LOG_D("end-file\n");
                                  is_loaded = true;
                                  _lv_demo_music_album_next(true);
                              }
                              else if (strcmp(event->valuestring, "playback-restart") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "idle") == 0)
                              {
                                  LOG_D("idle\n");
                              }
                          }
                      }
        
                      free(root);
                  }
                  //_msleep(250);
              }
              pthread_exit(NULL);
              LOG_D("pthread exit:\n");
          }
          这里通信都是采用JSON格式,使用了cJSON来解析送来的数据
              ``````
        
      3. 这些实现完成后,音乐播放器也就有了灵魂,就是一个真正的音乐播放器了,几乎支持所有音乐格式,我测试的常见的有wav,mp3,flac,ape,dts等
      4. 效果预览 (240*320) 项目位置:D1扩展板
      发布在 GUI
      X
      xfdr0805
    • Neza-D1开发板学习之按键和旋转编码器篇
      1. Neza-D1开发板上只引出一个按键,是使用的LRADC检检测按键的,想使用更多按键,可以使用板子上通过PCF8574扩展出来的IO,我画的扩展板使用了四个按键,分别为PP0,PP1,PP2,PP4

        KEY.png

      2. 设备树配置如下 内核需要勾选 Polled input device skeleton

         gpio_keys {
             compatible = "gpio-keys";
             pinctrl-names = "default";
             /*pinctrl-0 = <&key_pins>;*/
             #address-cells = <1>;
             #size-cells = <0>;
             autorepeat;
             button@0 {
                 label = "Key volume up";
                 linux,code = <KEY_VOLUMEUP>;
                 gpios = <&pcf8574 0 GPIO_ACTIVE_LOW>; /* PP0 */
             };
        
             button@1 {
                 label = "Key volume down";
                 linux,code = <KEY_VOLUMEDOWN>;
                 gpios = <&pcf8574 1 GPIO_ACTIVE_LOW>; /* PP1 */
             };
             
             button@2 {
                 label = "Key back";
                 linux,code = <KEY_BACK>;
                 gpios = <&pcf8574 2 GPIO_ACTIVE_LOW>; /* PP2 */
             };
        
             button@3 {
                 label = "Key enter";
                 linux,code = <KEY_ENTER>;
                 gpios = <&pcf8574 4 GPIO_ACTIVE_LOW>; /* PP4 */
             };
        
         };
        
      3. 编码器接在D1的PD14 PD15 设备树如下
        注意需要内核勾选<*> Polled GPIO Decoder Input driver

         rotary {
             compatible = "rotary-encoder";
             pinctrl-names = "default";
             /*pinctrl-0 = <&rotary_pins>;*/
             gpios = <&pio 3 14 GPIO_ACTIVE_LOW>, <&pio 3 15 GPIO_ACTIVE_LOW>; /* PD14, PD15 */
             linux,axis = <0>; /* REL_X */
             rotary-encoder,encoding = "gray";
             rotary-encoder,relative-axis;
         };
        

        然后在内核勾选

      4. 系统启动会有如下log

         [    0.111735] input: sunxi-keyboard as /devices/virtual/input/input0  这是板载的那个LRADC 按键
         [    1.668976] rotary-encoder soc@3000000:rotary: gray 这是旋转编码器
         [    1.675145] input: soc@3000000:rotary as /devices/platform/soc@3000000/soc@3000000:rotary/input/input1
         [    3.178668] input: soc@3000000:gpio_keys as /devices/platform/soc@3000000/soc@3000000:gpio_keys/input/input5 这是扩展出来的那4个按键
        
      5. 查看系统里已注册的输入设备,用这个比较清楚,ls /dev/input也可以

         # evtest 
         No device specified, trying to scan all of /dev/input/event*
         Available devices:
         /dev/input/event0:      sunxi-keyboard
         /dev/input/event1:      soc@3000000:rotary
         /dev/input/event2:      sunxi-ir
         /dev/input/event3:      audiocodec sunxi Audio Jack
         /dev/input/event4:      soc@3000000:gpio_keys
         /dev/input/event5:      ns2009_ts
        
      6. 可以使用evtest或hexdump来测试按键是否正常了

      7. 在代码使用就比较简单了

         #include <errno.h>
         #include <global.h>
         #include <linux/fcntl.h>
         #include <linux/input.h>
         #include <signal.h>
         #include <stdio.h> //-std=c99  -std=gnu99
         #include <stdlib.h>
         #include <string.h>
         #include <sys/types.h>
         #include <unistd.h>
        
         #define msleep(t) usleep((t)*1000)
         FILE *fp;
         int32_t fd_key;
         int32_t fd_encoder;
         struct input_event t;
         int main(int argc, char const *argv[])
         {
        
             fd_key = open("/dev/input/event1", O_RDONLY | O_NONBLOCK);
             if (fd_key < 0)
             {
                 fprintf(stderr, "error:can not open /dev/input/event1\n");
                 return -1;
             }
             fd_encoder = open("/dev/input/event4", O_RDONLY | O_NONBLOCK);
             if (fd_encoder < 0)
             {
                 fprintf(stderr, "error:can not open /dev/input/event4\n");
                 return -1;
             }
        
             while (1)
             {
        
                 // rotary encoder
                 if (read(fd_encoder, &t, sizeof(t)) == sizeof(t))
                 {
        
                     if (t.type == EV_REL)
                     {
                         LOG_D("ROTARY DIR: %d->%s\n", t.value, t.value == 1 ? "CC" : "CW");
                         rotary_encoder_handler(&u8g2, t.value);
                     }
                 }
                 // key
                 if (read(fd_key, &t, sizeof(t)) == sizeof(t))
                 {
                     if (t.type == EV_KEY)
                     {
                         switch (t.code)
                         {
                         case KEY_OK:
                             LOG_D("KEY_ENTER %s \n", t.value ? "Pressed" : "Released");
                             if (t.value == 0)
                                 rotary_encoder_button_handler(&u8g2);
                             break;
                         case KEY_SELECT:
                             LOG_D("KEY_SELECT %s \n", t.value ? "Pressed" : "Released");
                             break;
                         case KEY_VOLUMEUP:
                             LOG_D("KEY_VOLUMEUP %s \n", t.value ? "Pressed" : "Released");
                             if (t.value == 0)
                             {
                                 rotary_encoder_handler(&u8g2, -1);
                             }
        
                             break;
                         case KEY_VOLUMEDOWN:
                             LOG_D("KEY_VOLUMEDOWN %s \n", t.value ? "Pressed" : "Released");
                             if (t.value == 0)
                             {
                                 rotary_encoder_handler(&u8g2, 1);
                             }
                             break;
                         default:
                             break;
                         }
                     }
                 }
                 usleep(1);
             }
        
             return 0;
         }
        
      发布在 MR Series
      X
      xfdr0805
    • Neza-D1开发板学习之点灯篇
      1. 板子到手先来点个灯那是少不了的,先确定LED接在哪个引脚上,查看原理图,发现LED的座位号是U13,原来不是普通的LED,是一颗单总线的WS2812灯珠,这颗LED是接在PC0引脚上的,引脚标有LEDC-DO,是接在了LED控制器上,驱动官方已经做好了,我们直接用就可以

      2. 下边废话不多说,来看一下系统启动过程,系统已经把驱动给加载好了

         [    2.100847] sunxi_led_probe()1715 - start
         [    2.105375] sunxi_get_str_of_property()1560 - failed to get the string of propname led_regulator!
         [    2.115306] sunxi_register_led_classdev()1448 - led_classdev start
         [    2.126681] sunxi_led_probe()1820 - finish
        
      3. 来查看一下LED子系统下的灯

         # ls /sys/class/leds/
         blink         sunxi_led11g  sunxi_led3b   sunxi_led5r   sunxi_led8g
         sunxi_led0b   sunxi_led11r  sunxi_led3g   sunxi_led6b   sunxi_led8r
         sunxi_led0g   sunxi_led1b   sunxi_led3r   sunxi_led6g   sunxi_led9b
         sunxi_led0r   sunxi_led1g   sunxi_led4b   sunxi_led6r   sunxi_led9g
         sunxi_led10b  sunxi_led1r   sunxi_led4g   sunxi_led7b   sunxi_led9r
         sunxi_led10g  sunxi_led2b   sunxi_led4r   sunxi_led7g
         sunxi_led10r  sunxi_led2g   sunxi_led5b   sunxi_led7r
         sunxi_led11b  sunxi_led2r   sunxi_led5g   sunxi_led8b
        

        这里出现了很多LED,设备树默认是12个,这样子是不是很熟悉了,我们板子上的LED就是led0

         # ls /sys/class/leds/sunxi_led0b/
         brightness            power                 uevent
         device                subsystem             waiting_for_supplier
         max_brightness        trigger
        

        先来个红色的呼吸灯

         # echo heartbeat > /sys/class/leds/sunxi_led0r/trigger 
        

        再来个蓝色1秒闪烁一次

         # echo timer > /sys/class/leds/sunxi_led0b/trigger
        

        这灯真是亮瞎眼啊,查看亮度

         # cat  /sys/class/leds/sunxi_led0b/brightness 
         255
        

        调到50,这下好了

         # echo 50 > /sys/class/leds/sunxi_led0b/brightness
        

        把灯关掉

          # echo none > /sys/class/leds/sunxi_led0b/trigger
        

        查看支持哪些触发方式

         # cat  /sys/class/leds/sunxi_led0b/trigger 
         none rc-feedback rfkill-any rfkill-none kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock mmc0 mmc1 timer oneshot [mtd] nand-disk heartbeat backlight gpio cpu cpu0 activity default-on transient netdev pattern audio-mute audio-micmute
        
      4. 当然以上的操作不是操作的GPIO,而是使用的LED子系统,下边是用GPIO操作LED,这里的LED是在扩展板上引出的,引脚是通过PCF8574扩展出来的,具体可以看看D1原理图

      5. 使用SYSFS方式操作GPIO,在操作前先查看一下系统里未使用的GPIO,这里以PD18 BL—PWM为例

        # ls /sys/class/gpio/
        export        gpiochip0     gpiochip2020  unexport
        # PD18 = 32*3+18=114
        # echo 114 > export 
        

        执行成功 目录下会出现gpio114,查看一下内容,可以看到里边有direction,value

        # ls gpio114 
        active_low            edge                  uevent
        device                power                 value
        direction             subsystem             waiting_for_supplier
        

        接下来设置方向

        # echo out > gpio114/direction 
        

        输出高电平

        # echo 1 > gpio114/value
        

        输出低电平

        # echo 0 > gpio114/value
        

        不用的话执行

        # echo 114 > unexport
        

        查看一下,已经没有那个引脚导出的目录了

        # ls
        export        gpiochip0     gpiochip2020  unexport
        
      6. 使用GPIOD操作GPIO,先查询一下系统GPIO情况

         # gpiodetect 
         gpiochip0 [2000000.pinctrl] (224 lines)
         gpiochip1 [pcf8574] (8 lines)
         # gpioinfo gpiochip0 | grep -v unused | grep -v kernel
         gpiochip0 - 224 lines:
                 line   0:      unnamed      "sysfs"   input  active-high [used]
                 line   1:      unnamed      "sysfs"   input  active-high [used]
                 line   5:      unnamed      "sysfs"   input  active-high [used]
                 line  34:      unnamed  "interrupt"   input  active-high [used]
                 line 110:      unnamed "soc@3000000:rotary" input active-low [used]
                 line 111:      unnamed "soc@3000000:rotary" input active-low [used]
                 line 115:      unnamed  "usb1-vbus"  output  active-high [used]
                 line 116:      unnamed    "otg_det"   input  active-high [used]
                 line 117:      unnamed     "otg_id"   input  active-high [used]
                 line 118:      unnamed "soc@3000000:ir_send" output active-high [used]
                 line 144:      unnamed    "phy-rst"  output  active-high [used]
                 line 166:      unnamed         "cd"   input  active-high [used]
         # gpioinfo gpiochip1
         gpiochip1 - 8 lines:
                 line   0:      unnamed "Key volume up" input active-low [used]
                 line   1:      unnamed "Key volume down" input active-low [used]
                 line   2:      unnamed   "Key back"   input   active-low [used]
                 line   3:      unnamed      "blink"  output   active-low [used]
                 line   4:      unnamed  "Key enter"   input   active-low [used]
                 line   5:      unnamed        "led"  output  active-high [used]
                 line   6:      unnamed      "reset"  output   active-low [used]
                 line   7:      unnamed         "dc"  output   active-low [used]
        

        同样使用上边那个未使用的引脚为例 PD18 BL-PWM
        输出高电平

         # gpioset gpiochip0 114=1
        

        输出低电平

         # gpioset gpiochip0 114=0
        

        读取引脚电平

         # gpioget gpiochip0 114
         1
        

        就这么简单

      7. 以上操作还都未涉及到代码,下边开始用代码操作GPIO,代码仅供参考

        SYSFS方式

         #include <stdio.h>
         #include <stdlib.h>
         #include <string.h>
         #include <unistd.h>
         int main(void)
         {
             FILE *p = NULL;
             int i = 0;
             p = fopen("/sys/class/gpio/export", "w");
             fprintf(p, "%d", 38);
             fclose(p);
             p = fopen("/sys/class/gpio/gpio38/direction", "w");
             fprintf(p, "out");
             fclose(p);
             for (i = 0; i < 100; i++)
             {
                 p = fopen("/sys/class/gpio/gpio38/value", "w");
                 fprintf(p, "%d", 1);
                 sleep(1);
                 fclose(p);
                 p = fopen("/sys/class/gpio/gpio38/value", "w");
                 fprintf(p, "%d", 0);
                 sleep(1);
                 fclose(p);
             }
             p = fopen("/sys/class/gpio/unexport", "w");
             fprintf(p, "%d", 38);
             fclose(p);
             return 0;
         }
        

        GPIOD方式

         #include "gpiod/gpiod.h"
         #include <linux/fcntl.h>
         #include <signal.h>
         #include <stdio.h> -std=c99  -std=gnu99
         #include <stdlib.h>
         #include <string.h>
         #include <sys/time.h>
         #include <unistd.h>
         #define LED_PIN 148     PD8 32*3+18
         #define msleep(t) usleep((t)*1000)
        
         struct gpiod_chip *chip;
         struct gpiod_line *line;
         
         void _timer_handler(int n)
         {
             num++;
             gpiod_line_set_value(line, num % 2);
         }
         int main(int argc, char const *argv[])
         {
             struct gpiod_line_request_config config;
             int req;
             /* 1.打开 GPIO 控制器 */
             chip = gpiod_chip_open("/dev/gpiochip0");
             if (!chip)
                 return -1;
             /* 2.获取指定引脚 */
             line = gpiod_chip_get_line(chip, LED_PIN);
             if (!line)
             {
                 gpiod_chip_close(chip);
                 return -1;
             }
             /* 3.配置引脚输出  */
              req = gpiod_line_request_output(line, "blink", 0);
              if (req)
              {
                  fprintf(stderr, "gpio line request error.\n");
                  return -1;
              }
             signal(SIGALRM, _timer_handler);
             struct itimerval itv;
             itv.it_value.tv_sec = 1;
             itv.it_value.tv_usec = 1000; 
             itv.it_interval.tv_sec = 1;
             itv.it_interval.tv_usec = 0;
             setitimer(ITIMER_REAL, &itv, NULL);
             while (1)
             {
                 
             }
        
             return 0;
         }
        
      8. 到此点灯结束,感谢观看!

      发布在 MR Series
      X
      xfdr0805
    • Neza-D1开发板学习之SPI-LCD灯篇
      1. Neza-D1开发板芯片很强,但是没有引出RGB接口,板上引出了HDMI与MIPI接口,没引出RGB接口,手里的RGB接口的屏用不上了,扩展IO上有引出SPI1接口,看了一下尺寸,刚好可以放的下2.8”的SPI屏幕,正好手里有以前剩下的屏幕,就画了一块扩展板(可惜芯片是BGA封装,即使我能画出来,也没本事焊上啊!),驱动芯片是ili9341 4线SPI,又加了一个触摸芯片上去,如下图
        2021-09-10 22-23-50屏幕截图.png
      2. 设备树配置
          lcd_ili9341@0 {
      		#address-cells = <1>;
      		#size-cells = <1>;
      		compatible = "ilitek,ili9341";
      		reg = <0>;
      		spi-max-frequency = <32000000>;
      		rotation = <0>;
      		spi-cpol;
      		spi-cpha;
      		rgb;
      		fps = <30>;
      		buswidth = <8>;
      		txbuflen = <32768>;
      		reset-gpios = <&pcf8574 6 GPIO_ACTIVE_LOW>;
      		dc-gpios = <&pcf8574 7 GPIO_ACTIVE_LOW>;
      		/*backlight = <&backlight>;*/
      		led-gpios = <&pcf8574 5 GPIO_ACTIVE_HIGH>;
      		status = "okay";
          };
      
      1. 在内核打开small lcd support选项

        [] Staging drivers --->
        <
        > Support for small TFT LCD display modules --->3
        <*> FB driver for the ILI9341 LCD Controller

      2. 系统启动log

          [    2.670067] fbtft_of_value: buswidth = 8
          [    2.674512] fbtft_of_value: backlight = 39
          [    2.679097] fbtft_of_value: fps = 30
          [    2.683098] fbtft_of_value: txbuflen = 32768
          [    2.691259] GPT:Primary header thinks Alt. header is not at the end of the disk.
          [    2.699560] GPT:625184 != 7774207
          [    2.703265] GPT:Alternate GPT header not at the end of the disk.
          [    2.709981] GPT:625184 != 7774207
          [    2.713688] GPT: Use GNU Parted to correct GPT errors.
          [    2.719480]  mmcblk0: p1 p2 p3 p4
          [    2.987837] random: fast init done
          [    3.036258] graphics fb1: fb_ili9341 frame buffer, 240x320, 150 KiB video memory, 32 KiB buffer memory, fps=31, spi1.0 at 32 MHz
      
      1. 这里有一点需要注意,我们使用的GPIO是通过PCF8574扩展出来的,对IO读写操作时可能会导致休眠,因此不能在中断函数里使用,我们需要简单的修改一下驱动文件,用一个宏定义即可,如果不修改操作一次IO,就会出现一堆警告

        #define gpio_set_value gpio_set_value_cansleep

      2. 进系统里可以查看是否有对应设备

         # ls /dev/fb*
         /dev/fb0  /dev/fb1
         测试屏幕
         # cat /dev/urandom > /dev/fb1
         cat: write error: No space left on device
         清屏
         # cat /dev/zero > /dev/fb1
         cat: write error: No space left on device
         显示图片
         # fbv image1.jpg
        
      发布在 MR Series
      X
      xfdr0805

    xfdr0805 发布的最新帖子

    • 回复: D1 LicheeRV Dock 移植RTL8723DS驱动

      @tigger 在 D1 LicheeRV Dock 移植RTL8723DS驱动 中说:

      ifconfig -a

      是不是有显示 wlan0 和 wlan1?

      wlan1 是不是可以作为热点用?

      大佬,研究一下airkiss或者smartconfig,我在8723ds测试 不太稳定,有时可以配网成功 有时不行,不知道怎么修改

      发布在 MR Series
      X
      xfdr0805
    • 回复: Windows/Linux 给 lvgl8 添加本地文件系统支持。

      @whycan lvgl本身就支持吧,只要在lv_config.h配置一下就可以用了
      *---------------------

      • 3rd party libraries
        --------------------/

      /*File system interfaces for common APIs
      To enable set a driver letter for that API/
      #define LV_USE_FS_STDIO '\0' /Uses fopen, fread, etc/
      //#define LV_FS_STDIO_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */

      #define LV_USE_FS_POSIX 1 /Uses open, read, etc/
      #define CONFIG_LV_FS_POSIX_LETTER 'S' /Uses open, read, etc/
      //#define LV_FS_POSIX_PATH "/" /*Set the working directory. If commented it will be "./" */

      #define LV_USE_FS_WIN32 '\0' /Uses CreateFile, ReadFile, etc/
      //#define LV_FS_WIN32_PATH "C:\Users\john\" /*Set the working directory. If commented it will be ".\" */

      #define LV_USE_FS_FATFS '\0' /Uses f_open, f_read, etc/

      发布在 GUI
      X
      xfdr0805
    • 回复: 全志D1s可以申请样片了,本站网友开源原理图与PCB送三片D1s芯片(不能与其他网友作品雷同),还能送三片液晶显示屏

      @whycan
      谢晕哥 还是那个地址

      发布在 MR Series
      X
      xfdr0805
    • 回复: 全志D1s可以申请样片了,本站网友开源原理图与PCB送三片D1s芯片(不能与其他网友作品雷同),还能送三片液晶显示屏

      2021-11-15_223548.png 2021-11-15_223617.png

      F133_Board.pdf

      晕哥,现在还能申请芯片吗,验证OK后我会开源出来

      发布在 MR Series
      X
      xfdr0805
    • 动手让LVGL8带的Music-Demo音乐响起来(代码已上传)
      1. LVGL8 自带的有一个音乐播放器的Demo,效果不错,充分展示了LVGL8的弹性网格布局,动画等效果,来学习GUI是非常不错的,只是这个Demo并不能真正的播放音乐,但是已经把音乐播放的逻辑都给实现了,现在我们就动手给它注入灵魂----mpv
      2. 最初是用的sox这个号称音频界的瑞士军刀的工具,但是编写过程中碰到一个比较棘手的问题,那就是无法使用管道重定向输出(play本身不支持管道,只能借助sox),也就没办法获取播放进度,会造成进度条时间与实际播放时间有点误差,最后就放弃使用sox了

      2021-09-19_11-14.png

              if (pid == 0)
              {
                  LOG_D("child pid:%d\n", getpid());
                  char cmd[32];
                  prctl(PR_SET_PDEATHSIG, SIGKILL);
                  close(0);
                  dup2(pip[1], 1); //标准输出重定向到管道输出
                  close(pip[0]);
                  sprintf(buf, "./music/%s", _lv_demo_music_get_title(track_id));
                  sprintf(cmd, "--start=%d", _time, _lv_demo_music_get_track_length(track_id));
                  // execlp("ls", "ls", "./music", NULL);
                  // execlp("play", "play", "-p", buf, "trim", cmd, NULL);
                  // execlp("sox", "sox", buf, "-p", "|", "play", "-", "trim", cmd, NULL);
                  //最后知道怎么使用管道了,但是这是2个进程
                  // sox ./music/云非非\ -\ 邂逅.flac -t flac - | play -t flac - &
                  // sox ./music/云非非\ -\ 邂逅.flac -t flac - | play - &
                  // sox ./music/云非非\ -\ 邂逅.flac -p | play - &
                  return 0;
              } 
      
      1. 下边请出在后台默默工作的大佬MPV

        MPV播放器是什么?

        MPV是著名开源播放器mplayer和mplayer2的一个分支。
        mplayer则是这个地球上最强的播放器(没有之一),跨平台的特性使得windows、mac、linux上都可以见到它的身影,电脑、手机上很多播放器也是基于它开发的,由于mplayer不带界面,所以很多时候你都不知道是它在默默为你工作。
        并且mplayer播放视频时对于资源的消耗往往最少,所以你会发现在一台配置极差的电脑上播放高清电影,mplayer通常是最流畅的,使用快进时最能体现出差距,其他播放器已经画面卡死时,mplayer的画面可能只是感觉到掉帧而已。
        MPV播放器继承这些众多优良特性的同时,添加了内置于窗口的播放控制界面(OSC),对硬解的良好支持,以及其他额外特性。由于口碑很好,使得著名的mplayer前端:smplayer在不久前也添加了对MPV的支持,现在的smplayer你可以在mplayer和MPV2个核心之间切换使用。
        mpv官网的开发文档比较好,参考使用手册都可以使用起来

      2. 下边简单说一下过程
        a.首先要获取播放列表 播放列表里的字体是浪漫雅圆,使用freetype渲染的 。获取列表有2种方法
          方法1:
        
          int scan_music_list(char *_path)
          {
              char path[128];
              sprintf(path, "ls %s", _path);
              FILE *fp = popen(path, "r");
              if (ferror(fp))
              {
                  LOG_D("error\n");
              }
              char buf[128];
              while (!feof(fp))
              {
                  fgets(buf, sizeof(buf), fp);
                  strncpy(title_list[idx], buf, strlen(buf) - 1); //去掉文件名后的\n
                  get_music_info(title_list[idx], idx);
                  idx++;
                  usleep(1);
              }
              return idx;
          } 
          
        
         方法2:
        
         int list_dir(char *path, int depth)
         {
             DIR *dir;
             struct dirent *file;
             struct stat st;
             dir = opendir(path);
             if (!dir)
             {
                 LOG_D("open dir %s failed!", path);
                 return -1;
             }
             LOG_D("open dir %s ok!", path);
             while ((file = readdir(dir)) != NULL)
             {
                 if (strncmp(file->d_name, ".", 1) == 0 || strncmp(file->d_name, "..", 2) == 0)
                 {
                     continue;
                 }
                 strcpy(title_list[idx++], file->d_name);
                 if (stat(file->d_name, &st) >= 0 && S_ISDIR(st.st_mode) && depth <= 5)
                 {
                     list_dir(file->d_name, depth + 1);
                 }
             }
             closedir(dir);
             return 0;
         }      ```
        
        b.程序启动时创建一个子进程,一个线程获取播放进度,一个线程获取各种输入
          void lv_demo_music(void)
          {
              lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x343247), 0);
        
              music_num = scan_music_list(MUSIC_PATH);
              list = _lv_demo_music_list_create(lv_scr_act());
              ctrl = _lv_demo_music_main_create(lv_scr_act());
              pid = vfork();
              if (pid == 0)
              {
                  LOG_D("child pid:%d\n", getpid());
                  prctl(PR_SET_PDEATHSIG, SIGKILL);
                  execlp("mpv", "mpv", "--quiet", "--no-terminal", "--no-video", "--idle=yes", "--term-status-msg=", "--input-ipc-server=/tmp/mpvsocket", NULL);
                  LOG_D("child exit!\n");
                  return 0;
              }
              else if (pid > 0)
              {
                  LOG_D("parent pid:%d\n", getpid());
                  sleep(1);
                  close(0);
                  act.sa_handler = sigusr1;
                  sigfillset(&act.sa_mask);
                  act.sa_flags = SA_RESTART; /* don't fiddle with EINTR */
                  sigaction(SIGUSR1, &act, NULL);
                  addr.sun_family = AF_UNIX;
                  strcpy(addr.sun_path, "/tmp/mpvsocket");
                  fd_mpv = socket(AF_UNIX, SOCK_STREAM, 0);
                  if (fd_mpv == -1)
                  {
                      LOG_D("Create socket failed\n");
                  }
                  if (connect(fd_mpv, (struct sockaddr *)&addr, sizeof(addr)) == -1)
                  {
                      LOG_D("Cannot connect to socket %s\n", addr.sun_path);
                  }
                  if (pthread_create(&mthread, NULL, get_music_percent_pos, NULL) != 0)
                  {
                      LOG_D("pthread create error!\n");
                      return 0;
                  }
                  // LOG_D("get music pos pthread create ok!\n");
              }
              else
              {
                  LOG_D("fork error:\n");
              }
        
          #if LV_DEMO_MUSIC_AUTO_PLAY
              lv_timer_create(auto_step_cb, 1000, NULL);
          #endif
          }
        
          这里说明一下mpv里的参数
          "--quiet", //输出尽量少的信息
          "--no-terminal", //不接受终端输入
          "--no-video", //不需要视频
          "--idle=yes", //播放完不能出进程
          "--term-status-msg=", //状态信息不打印
          "--input-ipc-server=/tmp/mpvsocket"//使用sock方式与mpv通信
          这里使用的是mpv推荐的基于socket的JSON-based IPC protocol通信方式
        
        c. 一个线程启动时向mpv发送命令,然后监听事件就可以知道当前进度及状态
          void *get_music_percent_pos(void *arg)
          {
              // char cmd[] = "{\"command\": [ \"get_property\", \"playback-time\"] }\n";
              // char cmd[] = "{\"command\": [ \"get_property\", \"percent-pos\"] ,\"request_id\":2}\n";
              char cmd2[] = "{\"command\": [ \"observe_property\",2, \"percent-pos\"]}\n";
              char cmd1[] = "{\"command\": [  \"observe_property\",1,\"time-pos\"]}\n";
              char buf[512];
              cJSON *root;
              cJSON *event;
              cJSON *percent;
              write(fd_mpv, cmd1, strlen(cmd1));
              _msleep(100);
              write(fd_mpv, cmd2, strlen(cmd2));
              memset(buf, 0, sizeof(buf));
              while (1)
              {
        
                  if (read(fd_mpv, buf, sizeof(buf)) > 0)
                  {
                      //LOG_D("--->:%s\n", buf);
                      root = cJSON_Parse(buf);
                      if (root == NULL)
                      {
                          LOG_D("cJSON parse error!\n");
                          return;
                      }
                      // LOG_D("cJSON parse ok!\n");
                      if (cJSON_HasObjectItem(root, "event"))
                      {
        
                          event = cJSON_GetObjectItem(root, "event");
                          if (event != NULL)
                          {
                              if (strcmp(event->valuestring, "start-file") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "metadata-update") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "file-loaded") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "property-change") == 0 && (cJSON_GetObjectItem(root, "id")->valueint == 1))
                              {
                                  percent = cJSON_GetObjectItem(root, "data");
                                  lv_label_set_text_fmt(time_obj, "%02d:%02d", (int)percent->valuedouble / 60, (int)percent->valuedouble % 60);
                                  LOG_D("time pos:%d\n", (int)percent->valuedouble);
                              }
                              else if (strcmp(event->valuestring, "property-change") == 0 && (cJSON_GetObjectItem(root, "id")->valueint == 2))
                              {
                                  percent = cJSON_GetObjectItem(root, "data");
                                  lv_slider_set_value(slider_obj, (int)percent->valuedouble, LV_ANIM_ON);
                                  LOG_D("percent pos:%d\n", (int)percent->valuedouble);
                              }
                              else if (strcmp(event->valuestring, "end-file") == 0)
                              {
                                  LOG_D("end-file\n");
                                  is_loaded = true;
                                  _lv_demo_music_album_next(true);
                              }
                              else if (strcmp(event->valuestring, "playback-restart") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "idle") == 0)
                              {
                                  LOG_D("idle\n");
                              }
                          }
                      }
        
                      free(root);
                  }
                  //_msleep(250);
              }
              pthread_exit(NULL);
              LOG_D("pthread exit:\n");
          }
          这里通信都是采用JSON格式,使用了cJSON来解析送来的数据
              ``````
        
      3. 这些实现完成后,音乐播放器也就有了灵魂,就是一个真正的音乐播放器了,几乎支持所有音乐格式,我测试的常见的有wav,mp3,flac,ape,dts等
      4. 效果预览 (240*320) 项目位置:D1扩展板
      发布在 GUI
      X
      xfdr0805
    • 回复: 谁能提供一份XR829的固件 sdd_xr829.bin

      非常感谢!我下载试试

      发布在 MR Series
      X
      xfdr0805
    • 回复: 谁能提供一份XR829的固件 sdd_xr829.bin

      @dreamer
      我搜遍了,也没找到,Tina SDK 没能下载下来

      发布在 MR Series
      X
      xfdr0805
    • 回复: 给Neza-D1设计个扩展板

      扩展板上资源,除了外挂的声卡没调试好外,其它都OK了,既然不能注册声卡,那就应用层操作,反正D1上I2S配置好了,PCM5121通过I2C操作也很简单

      补上板子工程源文件 Kicad 5.99
      我发现Neza-D1开发板的这个引脚跟树莓派引脚兼容 😁
      今天才收到板子,焊接好后再补一张实物图片

      Neza-D1_expand_board.zip
      Neza-D1_expand_board.pdf
      2021-09-10 22-36-04屏幕截图.png
      2021-09-10 22-36-26屏幕截图.png

      发布在 MR Series
      X
      xfdr0805
    • Neza-D1开发板学习之SPI-LCD灯篇
      1. Neza-D1开发板芯片很强,但是没有引出RGB接口,板上引出了HDMI与MIPI接口,没引出RGB接口,手里的RGB接口的屏用不上了,扩展IO上有引出SPI1接口,看了一下尺寸,刚好可以放的下2.8”的SPI屏幕,正好手里有以前剩下的屏幕,就画了一块扩展板(可惜芯片是BGA封装,即使我能画出来,也没本事焊上啊!),驱动芯片是ili9341 4线SPI,又加了一个触摸芯片上去,如下图
        2021-09-10 22-23-50屏幕截图.png
      2. 设备树配置
          lcd_ili9341@0 {
      		#address-cells = <1>;
      		#size-cells = <1>;
      		compatible = "ilitek,ili9341";
      		reg = <0>;
      		spi-max-frequency = <32000000>;
      		rotation = <0>;
      		spi-cpol;
      		spi-cpha;
      		rgb;
      		fps = <30>;
      		buswidth = <8>;
      		txbuflen = <32768>;
      		reset-gpios = <&pcf8574 6 GPIO_ACTIVE_LOW>;
      		dc-gpios = <&pcf8574 7 GPIO_ACTIVE_LOW>;
      		/*backlight = <&backlight>;*/
      		led-gpios = <&pcf8574 5 GPIO_ACTIVE_HIGH>;
      		status = "okay";
          };
      
      1. 在内核打开small lcd support选项

        [] Staging drivers --->
        <
        > Support for small TFT LCD display modules --->3
        <*> FB driver for the ILI9341 LCD Controller

      2. 系统启动log

          [    2.670067] fbtft_of_value: buswidth = 8
          [    2.674512] fbtft_of_value: backlight = 39
          [    2.679097] fbtft_of_value: fps = 30
          [    2.683098] fbtft_of_value: txbuflen = 32768
          [    2.691259] GPT:Primary header thinks Alt. header is not at the end of the disk.
          [    2.699560] GPT:625184 != 7774207
          [    2.703265] GPT:Alternate GPT header not at the end of the disk.
          [    2.709981] GPT:625184 != 7774207
          [    2.713688] GPT: Use GNU Parted to correct GPT errors.
          [    2.719480]  mmcblk0: p1 p2 p3 p4
          [    2.987837] random: fast init done
          [    3.036258] graphics fb1: fb_ili9341 frame buffer, 240x320, 150 KiB video memory, 32 KiB buffer memory, fps=31, spi1.0 at 32 MHz
      
      1. 这里有一点需要注意,我们使用的GPIO是通过PCF8574扩展出来的,对IO读写操作时可能会导致休眠,因此不能在中断函数里使用,我们需要简单的修改一下驱动文件,用一个宏定义即可,如果不修改操作一次IO,就会出现一堆警告

        #define gpio_set_value gpio_set_value_cansleep

      2. 进系统里可以查看是否有对应设备

         # ls /dev/fb*
         /dev/fb0  /dev/fb1
         测试屏幕
         # cat /dev/urandom > /dev/fb1
         cat: write error: No space left on device
         清屏
         # cat /dev/zero > /dev/fb1
         cat: write error: No space left on device
         显示图片
         # fbv image1.jpg
        
      发布在 MR Series
      X
      xfdr0805
    • Neza-D1开发板学习之I2C-OLED篇
      1. Neza-D1开发板扩展IO上引出了I2C2,从原理图上看,已经接了PCF8574@38了,总线速度400K,我们的OLED也是接在这上边的,由于这屏幕比较小,显示不了多少东西,就不用FB方式了,直接在应用层操作OLED,所以设备树也不需要配置
        2021-09-10 22-07-43屏幕截图.png

      2. 在应用层操作就跟单片机操作是一样的了,这里为了方便操作屏幕,我分辨率移植u8g2这个库,我手里的哦了的是1.3"的,驱动IC是SHT1106,用1306也可以,只能显示不满屏,可能是基地址定义的不一样

      3. 我们不需要从OLED读取数据,所以只需要实现I2C写函数就可以,注意OLED模块上标的地址是0x78,这个是8位地址,这里使用的是7位地址,右移一位所以是0x3C

         unsigned char i2c_init()
         {
             const char *i2c_dev = "/dev/i2c-2";
             fd = open(i2c_dev, O_RDWR);
             if (fd < 0)
             {
                 printf("not have /dev/i2c-2 t\r\n");
                 fflush(stdout);
                 return -1;
             }
             return 0;
         }
         // I2C Addr 7bit
         unsigned char base_i2c_write(unsigned char device_addr, unsigned char *buff, int num)
         {
        
             struct i2c_rdwr_ioctl_data data;
             struct i2c_msg msgs_str[1];
             data.msgs = msgs_str;
             data.nmsgs = 1;
             data.msgs[0].len = num;
             data.msgs[0].addr = device_addr;
             data.msgs[0].flags = 0;
             data.msgs[0].buf = buff;
             ioctl(fd, I2C_RDWR, (unsigned long)&data);
         }
        
      4. 移植u8g2 非常简单,只需修改这下边这个函数里初始化位置和写数据位置就行,另外一个是延时,可以不用修改

         uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
         {
             /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
             static uint8_t buffer[128];
             static uint8_t buf_idx;
             uint8_t *data;
        
             switch (msg)
             {
             case U8X8_MSG_BYTE_INIT:
             {
                 /* add your custom code to init i2c subsystem */
                i2c_init; // I2C初始化
             }
             break;
        
             case U8X8_MSG_BYTE_START_TRANSFER:
             {
                 buf_idx = 0;
             }
             break;
        
             case U8X8_MSG_BYTE_SEND:
             {
                 data = (uint8_t *)arg_ptr;
        
                 while (arg_int > 0)
                 {
                     buffer[buf_idx++] = *data;
                     data++;
                     arg_int--;
                 }
             }
             break;
        
             case U8X8_MSG_BYTE_END_TRANSFER:
             {
                 //i2c写函数
                 if (base_i2c_write(OLED_ADDRESS, buffer, buf_idx) != 0)
                     return 0;
             }
             break;
        
             case U8X8_MSG_BYTE_SET_DC:
                 break;
        
             default:
                 return 0;
             }
        
             return 1;
         }
        
         uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
         {
             switch (msg)
             {
             case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
                 break;
             case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
                 break;
             case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
                 break;
             case U8X8_MSG_DELAY_I2C:      // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
                 break;                    // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
             case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
                 break;                    // arg_int=1: Input dir with pullup high for I2C clock pin
             case U8X8_MSG_GPIO_I2C_DATA:  // arg_int=0: Output low at I2C data pin
                 break;                    // arg_int=1: Input dir with pullup high for I2C data pin
             case U8X8_MSG_GPIO_MENU_SELECT:
                 u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0);
                 break;
             case U8X8_MSG_GPIO_MENU_NEXT:
                 u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0);
                 break;
             case U8X8_MSG_GPIO_MENU_PREV:
                 u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0);
                 break;
             case U8X8_MSG_GPIO_MENU_HOME:
                 u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0);
                 break;
             default:
                 u8x8_SetGPIOResult(u8x8, 1); // default return value
                 break;
             }
             return 1;
         }
        
      5. 这就移植完了,带中文字库,可以直接显示中文,非常方便,做一个显示信息的小玩意儿还是非常方便的,下边编译一下传到开发板里,可以看到显示成功(图片)

      6. 最后附上完整工程供参考
        neza_d1_u8g2_oled.7z

      发布在 MR Series
      X
      xfdr0805