导航

    全志在线开发者论坛

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

    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
    • 1 / 1