Navigation

    全志在线开发者论坛

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • 社区主页
    1. Home
    2. xfdr0805
    X
    • Profile
    • Following 0
    • Followers 1
    • my integral 1061
    • Topics 9
    • Posts 22
    • Best 2
    • Groups 0

    xfdr0805LV 5

    @xfdr0805

    1061
    integral
    6
    Reputation
    29
    Profile views
    22
    Posts
    1
    Followers
    0
    Following
    Joined Last Online

    xfdr0805 Unfollow Follow

    Best posts made by 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扩展板
      posted in 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;
         }
        
      posted in D1-H/D1s
      X
      xfdr0805

    Latest posts made by xfdr0805

    • Reply: 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/

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

      @whycan
      谢晕哥 还是那个地址

      posted in D1-H/D1s
      X
      xfdr0805
    • Reply: 全志D1s可以申请样片了,本站网友开源原理图与PCB送三片D1s芯片(不能与其他网友作品雷同),还能送三片液晶显示屏

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

      F133_Board.pdf

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

      posted in D1-H/D1s
      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扩展板
      posted in GUI
      X
      xfdr0805
    • Reply: 谁能提供一份XR829的固件 sdd_xr829.bin

      非常感谢!我下载试试

      posted in D1-H/D1s
      X
      xfdr0805
    • Reply: 谁能提供一份XR829的固件 sdd_xr829.bin

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

      posted in D1-H/D1s
      X
      xfdr0805
    • Reply: 给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

      posted in D1-H/D1s
      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
        
      posted in D1-H/D1s
      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

      posted in D1-H/D1s
      X
      xfdr0805
    • Neza-D1开发板学习之红外发送与接收篇
      1. D1芯片本身有硬件ir-tx ir-rx,但在开发板上通过扩展IO引出的只有IR-RX,IR-TX用不了,被I2C2占用了,只有用IO来发射红外信号了
        2021-09-10 22-02-07屏幕截图.png
      2. 设备对配置
        发射引脚,使用的是GPIO 需要在内核打开GPIO IR Bit Banging
         ir_send {
      		compatible  = "gpio-ir-tx";
      		gpios = <&pio 3 22 GPIO_ACTIVE_HIGH>; /* PD22 */
      		status = "okay";
      	};
          接收引脚
          &s_cir0 {
              pinctrl-names = "default", "sleep";
              pinctrl-0 = <&s_cir0_pins_a>;
              pinctrl-1 = <&s_cir0_pins_b>;
              linux,rc-map-name = "rc-tbs-nec";
              status = "okay";
          };
      

      下边是系统log

      [    1.719737] i2c /dev entries driver
      [    1.723748] IR NEC protocol handler initialized
      [    1.728825] IR RC5(x/sz) protocol handler initialized
      [    1.735217] rc rc0: GPIO IR Bit Banging Transmitter as /devices/platform/soc@3000000/soc@3000000:ir_send/rc/rc0
      [    1.746722] rc rc0: lirc_dev: driver gpio-ir-tx registered at minor = 0, no receiver, raw IR transmitter
      [    1.758445] sunxi_ir_startup: get ir protocol failed
      [    1.758467] (NULL device *): deviceless supply  not found, using dummy regulator
      [    1.772394] Registered IR keymap rc_map_sunxi
      [    1.777404] rc rc1: sunxi-ir as /devices/platform/soc@3000000/7040000.s_cir/rc/rc1
      [    1.786130] rc rc1: lirc_dev: driver sunxi-rc-recv registered at minor = 1, raw IR receiver, no transmitter
      [    1.797263] input: sunxi-ir as /devices/platform/soc@3000000/7040000.s_cir/rc/rc1/s_cir_rx
      
      1. 查看系统 /dev下的设备

         # ls /dev/lirc*
         /dev/lirc1  /dev/lirc1  /dev/lircd
        
      2. 此时查看/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
        

        此时如果用evtest测试按键是没有任何反应的,因为keymap我们没有修改成自己用的遥控器,当然就不上报事件了,但不影响后续操作

      3. 我们可以用lirc里的工具去测试硬件是否OK,测试之前要先修改默认配置文件,不修改的话,同样没反应

         # vi /etc/lirc/lirc_options.conf 
         driver          = default
         device          = /dev/lric1
         修改这2行就可以,里边driver默认是uinput,device默认是auto
         再次测试就有反应了
         # mode2 -m -d /dev/lirc1
         Using driver default on device /dev/lirc1
         Trying device: /dev/lirc1
         Using device: /dev/lirc1
         Warning: Running as root.
            9043     4402      626      497      656      499
             624      499      656      499      624      499
             656      499      624      499      624      531
             624     1630      619     1624      655     1624
             624     1624      624     1634      646     1624
             624     1624      655     1624      593     1656
             624      501      654     1624      593      534
             652     1624      624      499      625     1624
             656      504      619      499      655     1625
             624      499      656     1625      593      531
             624     1633      648      498      624     1624
             624    40035  
            9060     2184      624   143451-pulse   483450-space
        

        从这结果上可以明显的看出这些数值代表脉冲宽度,单位是us

      4. 使用irrecod -f -d /dev/lirc1 --disable-namespace来录制配置文件
        录制完成后放到 /etc/lric/liricd.conf.d/下边
        !注意,这里录制的结果不准确,需要借助上边的命令来获取每一个按键的值,这里只是借用一下配置文件的格式,里边的数字是右对齐
        示例:

         # Please take the time to finish this file as described in
         # https://sourceforge.net/p/lirc-remotes/wiki/Checklist/
         # and make it available to others by sending it to
         # <lirc@bartelmus.de>
         #
         # This config file was automatically generated
         # using lirc-0.10.1(default) on Thu Jan  1 01:37:06 1970
         # Command line used: -f -d /dev/lirc1 --disable-namespace
         # Kernel version (uname -r): 5.10.19
         #
         # Remote name (as of config file): elac
         # Brand of remote device, the thing you hold in your hand:
         # Remote device model nr:
         # Remote device info url:
         # Does remote device has a bundled capture device e. g., a
         #     usb dongle? :
         # For bundled USB devices: usb vendor id, product id
         #     and device string (use dmesg or lsusb):
         # Type of device controlled
         #     (TV, VCR, Audio, DVD, Satellite, Cable, HTPC, ...) :
         # Device(s) controlled by this remote:
        
         begin remote
        
         name  elac
         flags RAW_CODES|CONST_LENGTH
         eps            30
         aeps          100
        
         gap          108533
        
             begin raw_codes
        
                 name Power
                9043     4403      623      531      593      531
                 624      531      593      531      624      531
                 593      531      624      531      593      536
                 594     1680      592     1656      593     1656
                 625     1655      593     1666      582     1656
                 624     1656      593     1655      625      530
                 593      534      621      530      593     1656
                 624     1624      624      531      593      530
                 624      531      595     1655      623     1655
                 593     1656      593      530      624      531
                 593     1664      616     1656      593     1656
                 593
        
                 name BT
                9041     4407      622      531      593      531
                 593      562      593      531      593      531
                 625      533      596      525      624      530
                 593     1656      624     1656      593     1655
                 593     1664      616     1655      593     1656
                 593     1656      624     1658      591      530
                 624     1656      593      531      624      531
                 593      531      593      531      624     1661
                 587      530      624     1656      593      531
                 593     1687      593     1656      601     1648
                 624     1656      593      531      593     1656
                 624
        
             name Vol+
                9013     4437      592      530      593      562
                 593      531      624      531      593      531
                 594      565      589      530      593      531
                 624     1656      593     1656      624     1665
                 590     1651      591     1656      624     1655
                 593     1656      593     1667      613      531
                 593     1656      624     1656      593      531
                 593      562      595      528      593     1656
                 624      531      593     1656      624      531
                 593      530      593     1659      621     1655
                 593     1656      593      562      593     1656
                 593
                 
             name Vol-
                9044     4436      592      531      624      531
                 593      533      622      530      593      531
                 593      531      624      531      593      530
                 624     1656      593     1661      595     1679
                 593     1655      593     1656      624     1656
                 593     1667      582     1659      620      530
                 593     1656      624     1656      600      524
                 624     1656      593      531      593      531
                 624      531      593     1656      624      531
                 598      530      620     1654      593      531
                 624     1625      624     1656      593     1666
                 614
        
             name Pre
                9048     4403      592      562      593      530
                 593      562      593      530      593      531
                 624      531      593      531      624      531
                 593     1661      593     1680      592     1656
                 593     1656      624     1656      603     1650
                 588     1656      623     1656      593     1656
                 624     1662      587     1655      593      531
                 624      531      593      531      624     1656
                 593      531      631      524      593      530
                 593      562      593     1656      593     1656
                 624     1663      586      530      624     1624
                 624
                 .
                 .
                 .
                 .
                 .
             end raw_codes
        
         end remote
        
      5. 接下来使用irw来验证刚才录制的是否正确

         # irw
         lircd-0.10.1[432]: Notice: accepted new client on /var/run/lirc/lircd
         lircd-0.10.1[432]: Info: [lirc] protocol is enabled
         0000000000000001 00 Power elac
         0000000000000002 00 BT elac
         0000000000000003 00 Vol+ elac
         0000000000000004 00 Vol- elac
         0000000000000008 00 Ana1 elac
         000000000000000a 00 Opt1 elac
         000000000000000b 00 Opt2 elac
        
      6. 然后在/etc/lirc/目录下创建lircrc配置文件或~/.lircrc
        格式如下

         begin
                 prog = irexec
                 button = Power
                 repeat = 0
                 config = echo "power"
         end
         begin
                 prog = irexec
                 button = Vol+
                 repeat = 1
                 config = amixer -M -c 0 sset 'Headphone',0 1%+ > /dev/null
         end
         begin
                 prog = irexec
                 button = Vol-
                 repeat = 1
                 config = amixer -M -c 0 sset 'Headphone',0 1%- > /dev/null
         end
        

        保存

      7. 最后就是使用irexec来进行各种操作了,无需编程,到这里可以自由发挥了,想要集成到代码里也很简单,看下图

         # irexec 
         lircd-0.10.1[448]: Notice: accepted new client on /var/run/lirc/lircd
         lircd-0.10.1[448]: Info: [lirc] protocol is enabled
         
         volume up click
         Simple mixer control 'Headphone',0
         Capabilities: pvolume pvolume-joined pswitch
         Playback channels: Front Left - Front Right
         Limits: Playback 0 - 63
         Mono:
         Front Left: Playback 47 [54%] [-16.00dB] [on]
         Front Right: Playback 47 [54%] [-16.00dB] [on]
        
         volume down click
         Simple mixer control 'Headphone',0
         Capabilities: pvolume pvolume-joined pswitch
         Playback channels: Front Left - Front Right
         Limits: Playback 0 - 63
         Mono:
         Front Left: Playback 45 [50%] [-18.00dB] [on]
         Front Right: Playback 45 [50%] [-18.00dB] [on]
        
      8. LIRC处理流程

                +--------+         +-------------+                     +--------+
                |        |         | Linux input |                     | Appli- |
         --->---| kernel |---->----| layer       |---------->----------| cation |
                |        |         |             |  /dev/input/eventX  |        |
                +--------+         +-------------+                     +--------+
        
        
                +--------+         +-------------+
                |        |         | Linux input |
         --->---| kernel |---->----| layer       |
                |        |         |             |
                +--------+         +-------------+
                                            |
                                            v
                                            |
                                            |                           +--------+
                                    +-------------+                     | Appli- |
                                    |    lirc     |---------->----------| cation |-+
                                    |             |     lirc socket     |        | |
                                    +-------------+                     +--------+ |-+
                                                                            |        | |
                                                                            +--------+ |
                                                                            |        |
                                                                            +--------+
        
                +--------+         +-------------+                     +--------+
                |        |         |             |                     | Appli- |
         --->---| kernel |---->----|    lirc     |---------->----------| cation |-+
                |        |         |             |     lirc socket     |        | |
                +--------+         +-------------+                     +--------+ |-+
                                                                            |        | |
                                                                            +--------+ |
                                                                            |        |
                                                                            +--------+
        
      9. 通过socket使用lirc,不需要引入任何文件,可以方便的集成到代码里,就是上述第三种方式

        struct sigaction act;
        char buf[128];
        struct sockaddr_un addr;
        typedef struct
        {
            char addr[32];
            char code[8];
            char type[16];
            char name[16];
        } IRW_DATA;
        IRW_DATA irw_data;
        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, "/var/run/lirc/lircd");
        fd_lircd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
        if (fd_lircd == -1)
        {
            printf("Create socket failed\n");
        }
        if (connect(fd_lircd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
        {
            printf("Cannot connect to socket %s\n", addr.sun_path);
        }
        while (1)
        {
            // lircd
            if (read(fd_lircd, buf, 128) > 0)
            {
                LOG_D("%s\n", buf);
                char *p = strtok(buf, " ");
                int i = 0;
                while (p)
                {
                    // LOG_D("%s\n", p);
                    if (i == 0)
                        strcpy(irw_data.addr, p);
                    else if (i == 1)
                        strcpy(irw_data.code, p);
                    else if (i == 2)
                        strcpy(irw_data.type, p);
                    else if (i == 3)
                        strcpy(irw_data.name, p);
                    p = strtok(NULL, " ");
                    i++;
                }
                LOG_D("%s\n", irw_data.type);
                switch ((uint8_t)strtol(irw_data.addr, NULL, 10))
                {
                case Vol_up:
                    rotary_encoder_handler(&u8g2, -1);
                    break;
                case Vol_down:
                    rotary_encoder_handler(&u8g2, 1);
                    break;
                case Play:
                    rotary_encoder_button_handler(&u8g2);
                    break;
                case Bt:
                    printf("BT button\n");
                    break;
                }
            }
        }
        
      10. 下边开始向外发射IR信号,发射就使用之前录制的配置文件

        irsend SEND_ONCE elac power
        
      posted in D1-H/D1s
      X
      xfdr0805