导航

    全志在线开发者论坛

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

    给大家介绍种控制ledc驱动器的方法 轻松100fps

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

      我之前玩那个ledc驱动器嘛,tina系统里面的那个其实不太好用,每次要echo 数据去那个device是不行的,一个指令只能设置一个灯的1/3,那么100fps 设置80个灯就要炸啦,系统直接宕机

      后来我找啦个办法直接往内核发数据,爽歪歪啦

      操作那个灯的方法现在变成了

      1 准备好80个灯的数据
      2 发送给内核驱动
      3 驱动读取数据 交给DMA 一次写80个灯的数据

      那么800k的 ws2812的数据波,算下来80个灯一次传送数据大概在 2ms,抛开数据准备的时间也就3ms 80个灯的数据,加个sleep,一帧10ms都是绰绰有余的,那么100fps,完全没问题,使用方法也很简单,啥额外的驱动读取都不需要,直接往内核发数据包,内核驱动接就好啦

      亲测效果很好
      B站演示视频

      【RGB只要开始玩就停不下来,这个全志的ledc用起来终于顺心了,DMA传输数据,反正跑个100fps没有一点压力哪位大佬有玩过lua代码在线编译执行的呀我…】 https://www.bilibili.com/video/BV1TJ4m1h7Gk/?share_source=copy_web

      我是直接通过netlink去实现往内核驱动写数据的

      所以得先打开 tina linux里面 lib中相关的库

      make menuconfig

      Libraries 里面涉及到netlink的库给它打开以后就可以用啦

      Symbol: PACKAGE_libnftnl [=y]                                                                                                        │  
        │ Type  : tristate                                                                                                                     │  
        │ Prompt: libnftnl........... Low-level netlink library for the nf_tables subsystem                                                    │  
        │   Location:                                                                                                                          │  
        │     -> Libraries                                                                                                                     │  
        │   Defined at tmp/.config-package.in:20672                                                                                            │  
        │   Selects: PACKAGE_libmnl [=y] && PACKAGE_libpthread [=y] && PACKAGE_librt [=y] && PACKAGE_libc [=y] && PACKAGE_libssp [=n]          │  
        │   Selected by: PACKAGE_nftables [=n] && IPV6 [=y]                                                                                    │  
        │                                                              
      

      然后就是在ledc驱动里面加上自己的代码

      /lichee/linux-5.4/drivers/leds/leds-sunxi.c

      
      
      
      //leo 添加个模块
      
      
      
      #define NETLINK_TEST 25
      #define MSG_SIZE 320
      
      static struct sock *nl_sk = NULL;
      u32 leo_led_data[80];
      //用于触发数据写入
      static int leo_show_led(void)
      {
      	struct sunxi_led *led = sunxi_led_global;
      	unsigned long flags;
      
      	int i,err;
      	
      	led->reset_ns	=	84;
      	
      	//开始用DMA方式传输数据
      
      	spin_lock_irqsave(&led->lock, flags);
      	
      	memcpy(led->data,leo_led_data, sizeof(leo_led_data));
      	led->length = 80;
      
      	spin_unlock_irqrestore(&led->lock, flags);
      
      	/* prepare for dma xfer, dynamic apply dma channel */
      	if (led->length > SUNXI_LEDC_FIFO_DEPTH) {
      		err = sunxi_ledc_dma_get(led);
      		if (err)
      			return err;
      	}
      
      	sunxi_ledc_trans_data(led);
      	if (debug_mask & DEBUG_INFO2) {
      		dprintk(DEBUG_INFO2, "dump reg:\n");
      		led_dump_reg(led, 0, 0x30);
      	}
      
      	sunxi_ledc_complete(led);
      
      	/* dynamic release dma chan, release at the end of a transmission */
      	if (led->length > SUNXI_LEDC_FIFO_DEPTH)
      		sunxi_ledc_dma_put(led);
      
      	
      	kfree(led);
      
      	//printk(KERN_INFO,"leo_show_led------end\n");
      
      	return 0;
      	
      }
      
      int send_nl_message(struct sock *sock, int pid, char *message, int message_len) 
      {
          struct sk_buff *skb_out;
          struct nlmsghdr *nlh_out;
      
          // Allocate a new skb
          // 创建一个 sk_buff 缓冲区
          skb_out = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
          if (!skb_out) {
              return -ENOMEM;
          }
      
          // Initialize the netlink message header
          nlh_out = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, message_len, 0);
          if (!nlh_out) {
              nlmsg_free(skb_out);
              return -ENOMEM;
          }
      
          // Set the destination PID and copy the message data
          NETLINK_CB(skb_out).dst_group = 0; /* unicast */
          memcpy(NLMSG_DATA(nlh_out), message, message_len);
      
          // Send the message to the specified PID
          // 向指定的 Netlink Socket 发送消息
          if (nlmsg_unicast(sock, skb_out, pid) < 0) {
              nlmsg_free(skb_out);
              return -EFAULT;
          }
      
          return 0;
      }
      
      static void nl_recv_msg(struct sk_buff *__skb)
      {
          struct sk_buff *skb;
          char str[MSG_SIZE] = {0};
          struct nlmsghdr *nlh;
      	int i;
      	
      
          if (__skb == NULL) {
              return;
          }
      
          skb = skb_get(__skb);
          if (skb->len < NLMSG_SPACE(0)) {
              return;
          }
      
          nlh = nlmsg_hdr(skb);
      
      
      	/* 时间一长就崩掉啦,奇怪??????*/
      	leo_show_led();
      
      
      	kfree(nlh);
      	kfree(skb);
      
      
      
      }
      
      
      static int __init netlink_init(void)
      {
          struct netlink_kernel_cfg cfg = {
              .input = nl_recv_msg,
          };
      
          nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);
          if (!nl_sk) {
              printk(KERN_ALERT "Failed to create netlink socket.\n");
              return -ENOMEM;
          }
      
          printk(KERN_INFO "Netlink socket created.\n");
      
          return 0;
      }
      
      static void __exit netlink_exit(void)
      {
          netlink_kernel_release(nl_sk);
          printk(KERN_INFO "Netlink socket released.\n");
      }
      
      module_init(netlink_init);
      module_exit(netlink_exit);
      
      MODULE_LICENSE("GPL");
      
      /*debug
      
      
      
      sunxi_ledc_irq_handler()1313 - wait time is more than 600000 ns,going to reset ledc and drop this operation!
      
      
      
      */
      
      
      
      

      上面那个相当于内核里面的服务端,功能也很简单,就是接收数据,收到后直接丢给dma,发送出去

      下面这个是客户端,也就是你的app
      大概写个发送数据的代码就行啦,超级简单,哈哈哈

      大概的代码如下
      大家可以参考下

      #include <string.h>
      #include <sys/socket.h>
      #include <linux/netlink.h>
      
      
      // net 方式往内核发送信息
      
          int sock_fd, len;
          struct sockaddr_nl src_addr, dst_addr;
          struct nlmsghdr * nlh;
          char msg[MSG_SIZE] = {0};
      
          sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
          if(sock_fd < 0) {
              perror("socket");
              exit(EXIT_FAILURE);
          }
          /**/
          memset(&src_addr, 0, sizeof(struct sockaddr_nl));
          src_addr.nl_family = AF_NETLINK;
          src_addr.nl_pid    = getpid();
      
          bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(struct sockaddr_nl));
      
          memset(&dst_addr, 0, sizeof(struct sockaddr_nl));
          dst_addr.nl_family = AF_NETLINK;
          dst_addr.nl_pid    = 0; // send to kernel
          dst_addr.nl_groups = 0; // unicast
      
          nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MSG_SIZE));
          memset(nlh, 0, NLMSG_SPACE(MSG_SIZE));
          nlh->nlmsg_len   = NLMSG_SPACE(MSG_SIZE);
          nlh->nlmsg_pid   = getpid();
          nlh->nlmsg_flags = 0;
      
          char buf[MAX_PAYLOAD] = {0};
          // sprintf(cmd, "echo 10 > /sys/class/leds/sunxi_led79r/brightness");
          // system(cmd);
          // usleep(100000);
      
          // net 方式往内核发送信息
      
          // 测试颜色
          struct color_rgb led_color_test;
          // led_color_test.r=   1;
          // led_color_test.g=   1;
          // led_color_test.b=   1;
      
          struct color_hsv color_hsv_test;
          u_int16_t test_h = 0;
          color_hsv_test   = color_hsv(100, 250, 0);
      
          //启动全led检查
          check_all_led(sock_fd,nlh,dst_addr);
      
      
      //按键全功能检查
      void check_all_led(int sock_fd,struct nlmsghdr * nlh,struct sockaddr_nl  dst_addr){
          struct color_hsv color_hsv_test;
          u_int16_t test_h = 0;
          color_hsv_test   = color_hsv(33, 235, 0);
      
          //用于提示灯颜色   color_hsv(33, 235, 200);
      
          int is_stop=0;
          int len = sizeof(led_data);
      
          int    bright2=0;
          u_int16_t t_flag=   0;
          printf("start check led!\n");
          while(!is_stop){
      
              if(t_flag == 0) {
                  bright2 = bright2 + 4;
              } else {
                  bright2 = bright2 - 4;
                  test_h++;
              }
              if(bright2 > 225) {
                  bright2 = 220;
                  t_flag  = 1;
                  usleep(40000);
              }
              if(bright2 < 0) {
                  bright2 = 0;
                  //t_flag  = 0;
                  is_stop=1;//放完停掉
                  //usleep(50000);
                  printf("stop led\n");
              }
              color_hsv_test.v    =   bright2;
              for(int i = 0; i < 80; i++) {
      
                  led_data[i] = trans_hsv_u32(color_hsv_test);
              }
      
      
              memcpy(NLMSG_DATA(nlh), (const uint8_t *)led_data, len);
              if(sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dst_addr, sizeof(struct sockaddr_nl)) < 0) {
                  perror("sendto");
                  exit(EXIT_FAILURE);
              } else {
                  //printf("send ok\n");
              }
              usleep(12000);
              if(is_stop==1){
                  break;
                  return;
              }
          }
      
      }
      
      
      
      

      这里面有用的就是个 sendto函数

      其他都是用来准备好数据包的,我这直接把数据整个发过去了
      其实这么整效率不够高,最好是就发个数据包的地址过去
      然后接收的位置,直接读那个地址里面的数据就行了

      反正即便是这么写,跑起来也很快,基本不耗什么cpu

      主要是简单啊,不用去理解linux底层驱动了,反正我也没搞明白怎么驱动的
      反正就是用了netlink直接往内核驱动发包,内核驱动收到包以后直接执行。。。
      直接执行,达到结果也是一样快。

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

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

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