给大家介绍种控制ledc驱动器的方法 轻松100fps
-
我之前玩那个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直接往内核驱动发包,内核驱动收到包以后直接执行。。。
直接执行,达到结果也是一样快。
Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号