APPS:IGKBoard-IMX6ULL: Add test-apps source code:
Signed-off-by: guowenxue <guowenxue@gmail.com>
13 files added
1 files modified
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2024 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: can_test.c |
| | | * Description: This file is socket CAN loop test program |
| | | * |
| | | * Version: 1.0.0(05/26/2024) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "05/26/2024 05:42:49 PM" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <string.h> |
| | | #include <unistd.h> |
| | | #include <sys/ioctl.h> |
| | | #include <net/if.h> |
| | | #include <sys/socket.h> |
| | | #include <linux/can.h> |
| | | #include <linux/can/raw.h> |
| | | #include <getopt.h> |
| | | |
| | | // 打印使用帮助信息 |
| | | void print_usage(const char *progname) |
| | | { |
| | | printf("Usage: %s -i <can_interface> -m <mode>\n", progname); |
| | | printf("Options:\n"); |
| | | printf(" -i, --interface CAN interface (e.g., can0)\n"); |
| | | printf(" -m, --mode Mode: send or receive\n"); |
| | | printf(" -h, --help Display this help message\n"); |
| | | } |
| | | |
| | | void send_can_message(const char *ifname) |
| | | { |
| | | int fd; |
| | | struct sockaddr_can addr; |
| | | struct ifreq ifr; |
| | | struct can_frame frame; |
| | | |
| | | // 创建socket |
| | | fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); |
| | | if (fd < 0) |
| | | { |
| | | perror("socket"); |
| | | exit(1); |
| | | } |
| | | |
| | | // 指定CAN接口 |
| | | strcpy(ifr.ifr_name, ifname); |
| | | ioctl(fd, SIOCGIFINDEX, &ifr); |
| | | addr.can_family = AF_CAN; |
| | | addr.can_ifindex = ifr.ifr_ifindex; |
| | | |
| | | // 绑定socket到CAN接口 |
| | | if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) |
| | | { |
| | | perror("bind"); |
| | | exit(1); |
| | | } |
| | | |
| | | // 构造CAN帧 |
| | | frame.can_id = 0x123; // 设置CAN ID |
| | | frame.can_dlc = 2; // 数据长度 |
| | | frame.data[0] = 0x11; // 数据 |
| | | frame.data[1] = 0x22; // 数据 |
| | | |
| | | // 发送CAN帧 |
| | | if (write(fd, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) |
| | | { |
| | | perror("write"); |
| | | exit(1); |
| | | } |
| | | |
| | | // 关闭socket |
| | | close(fd); |
| | | } |
| | | |
| | | void receive_can_message(const char *ifname) |
| | | { |
| | | int fd; |
| | | struct sockaddr_can addr; |
| | | struct ifreq ifr; |
| | | struct can_frame frame; |
| | | |
| | | // 创建socket |
| | | fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); |
| | | if (fd < 0) { |
| | | perror("socket"); |
| | | exit(1); |
| | | } |
| | | |
| | | // 指定CAN接口 |
| | | strcpy(ifr.ifr_name, ifname); |
| | | ioctl(fd, SIOCGIFINDEX, &ifr); |
| | | addr.can_family = AF_CAN; |
| | | addr.can_ifindex = ifr.ifr_ifindex; |
| | | |
| | | // 绑定socket到CAN接口 |
| | | if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) |
| | | { |
| | | perror("bind"); |
| | | exit(1); |
| | | } |
| | | |
| | | // 接收CAN帧 |
| | | while (1) |
| | | { |
| | | int nbytes = read(fd, &frame, sizeof(struct can_frame)); |
| | | if (nbytes < 0) |
| | | { |
| | | perror("read"); |
| | | exit(1); |
| | | } |
| | | |
| | | if (nbytes < sizeof(struct can_frame)) |
| | | { |
| | | fprintf(stderr, "read: incomplete CAN frame\n"); |
| | | exit(1); |
| | | } |
| | | |
| | | // 打印接收到的CAN帧 |
| | | printf("Received CAN frame: ID=0x%X DLC=%d data=", frame.can_id, frame.can_dlc); |
| | | for (int i = 0; i < frame.can_dlc; i++) |
| | | printf("%02X ", frame.data[i]); |
| | | |
| | | printf("\n"); |
| | | } |
| | | |
| | | // 关闭socket |
| | | close(fd); |
| | | } |
| | | |
| | | int main(int argc, char **argv) |
| | | { |
| | | int opt, index = 0; |
| | | const char *ifname = NULL; |
| | | const char *mode = NULL; |
| | | |
| | | // 定义长选项 |
| | | static struct option long_options[] = |
| | | { |
| | | {"interface", required_argument, 0, 'i'}, |
| | | {"mode", required_argument, 0, 'm'}, |
| | | {"help", no_argument, 0, 'h'}, |
| | | {0, 0, 0, 0} |
| | | }; |
| | | |
| | | while ((opt = getopt_long(argc, argv, "i:m:h", long_options, &index)) != -1) |
| | | { |
| | | switch (opt) { |
| | | case 'i': |
| | | ifname = optarg; |
| | | break; |
| | | case 'm': |
| | | mode = optarg; |
| | | break; |
| | | case 'h': |
| | | print_usage(argv[0]); |
| | | return 0; |
| | | default: |
| | | print_usage(argv[0]); |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | if (ifname == NULL || mode == NULL) |
| | | { |
| | | print_usage(argv[0]); |
| | | return 1; |
| | | } |
| | | |
| | | if (strcmp(mode, "send") == 0) |
| | | { |
| | | send_can_message(ifname); |
| | | } |
| | | else if (strcmp(mode, "receive") == 0) |
| | | { |
| | | receive_can_message(ifname); |
| | | } |
| | | else |
| | | { |
| | | fprintf(stderr, "Invalid mode: %s\n", mode); |
| | | print_usage(argv[0]); |
| | | return 1; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | /* 在C语言编程时,一般系统的头文件用<xxx.h>,我们自己写的头文件则用"zzz.h" */ |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <unistd.h> |
| | |
| | | #include <time.h> |
| | | #include <errno.h> |
| | | |
| | | /* 在C语言编程中,函数应该先定义再使用,如果函数的定义在函数调用后面,应该前向声明。*/ |
| | | int ds18b20_get_temperature(float *temp); |
| | | |
| | | int main(int argc, char *argv[]) |
| | | { |
| | | float temp; /* 温度值有小数位,所以使用浮点数 */ |
| | | |
| | | /* 1,在Linux下做C语言编程时,函数返回值一般是0表示成功,<0表示失败,我们也遵循这个规约; |
| | | * 2,但函数调用只能有一个返回值,所以这里的采样函数只能通过指针来返回采样的温度值; |
| | | * 3,因为要在ds18b20_get_temperature()函数中修改main()中temp的值,所以这里传&temp; |
| | | */ |
| | | if( ds18b20_get_temperature(&temp) < 0 ) |
| | | { |
| | | printf("ERROR: ds18b20 get temprature failure\n"); |
| | |
| | | int ds18b20_get_temperature(float *temp) |
| | | { |
| | | const char *w1_path = "/sys/bus/w1/devices/"; |
| | | char ds_path[50]; /* DS18B20 采样文件路径 */ |
| | | char chip[20]; /* DS18B20 芯片序列号文件名 */ |
| | | char buf[128]; /* read() 读数据存储 buffer */ |
| | | DIR *dirp; /* opendir()打开的文件夹句柄 */ |
| | | struct dirent *direntp; /* readdir()读文件夹内容时的目录项*/ |
| | | int fd =-1; /* open()打开文件的文件描述符 */ |
| | | char *ptr; /* 一个字符指针,用来字符串处理 */ |
| | | int found = 0; /* 是否找到DS18B20的标志,默认设置为没找到(0) */ |
| | | int rv = 0; /* 函数返回值,默认设置为成功返回(0) */ |
| | | char ds_path[50]; |
| | | char chip[20]; |
| | | char buf[128]; |
| | | DIR *dirp; |
| | | struct dirent *direntp; |
| | | int fd =-1; |
| | | char *ptr; |
| | | int found = 0; |
| | | int rv = 0; |
| | | |
| | | |
| | | /* 在C语言编程时,进入函数的第一件事应该进行函数参数的合法性检测,检查参数非法输入。 |
| | |
| | | /* 文件夹打开用完后,要记得第一时间关闭 */ |
| | | closedir(dirp); |
| | | |
| | | /* found在定义时初始化为0,如果上面的代码没有找到 "28-" 文件名则其值依然为0,否则将会被 |
| | | * 设置为1。如果 found 的值为0的话,则打印错误信息并返回相应的错误码-3. |
| | | */ |
| | | /* found在定义时初始化为0,如果上面没有找到 "28-" 文件则其值依然为0,否则将被置为1 */ |
| | | if( !found ) |
| | | { |
| | | printf("Can not find ds18b20 in %s\n", w1_path); |
| | | return -3; |
| | | } |
| | | |
| | | /* 使用snprintf()函数生成完整路径/sys/bus/w1/devices/28-xxxxx/w1_slave |
| | | * 并保存到 ds_path 中。 |
| | | */ |
| | | /* 使用snprintf() 生成完整路径/sys/bus/w1/devices/28-xxxxx/w1_slave */ |
| | | snprintf(ds_path, sizeof(ds_path), "%s/%s/w1_slave", w1_path, chip); |
| | | |
| | | /* 接下来打开 DS18B20 的采样文件,如果失败则返回相应的错误码-4。 */ |
| | | /* 接下来打开 DS18B20 的采样文件 */ |
| | | if( (fd=open(ds_path, O_RDONLY)) < 0 ) |
| | | { |
| | | printf("open %s error: %s\n", ds_path, strerror(errno)); |
| | |
| | | { |
| | | printf("read %s error: %s\n", ds_path, strerror(errno)); |
| | | |
| | | /* 1, 这里不能直接调用 return -5 直接返回,否则的话前面open()打开的文件描述符就没有关闭。 |
| | | /* 1, 这里不能直接调用 return直接返回,否则的话前面open()打开的文件描述符就没有关闭。 |
| | | * 这里设置 rv 为错误码-5,通过 goto 语句跳转到函数后面统一进行错误处理。 |
| | | * |
| | | * 2, 在C语言编程时我们应该慎用goto语句进行"随意"的跳转,因为它会降低代码的可读性。但这里是 |
| | | * goto语句的一个非常典型应用,我们经常会用它来对错误进行统一的处理。 |
| | | * |
| | | * 3,goto后面的cleanup为标号,它在下面的代码中定义。 |
| | | */ |
| | | rv = -5; |
| | | goto cleanup; |
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2021 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: hello.c |
| | | * Description: This file is hello world test program. |
| | | * |
| | | * Version: 1.0.0(2021-12-10) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "2021-12-10 22:41:49" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | |
| | | int main (int argc, char **argv) |
| | | { |
| | | printf("Hello, LingYun IoT System Studio.\n"); |
| | | |
| | | return 0; |
| | | } |
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2021 Guo Wenxue<Email:guowenxue@gmail.com QQ:281143292> |
| | | * All rights reserved. |
| | | * |
| | | * Filename: keypad.c |
| | | * Description: This file used to test GPIO button driver builtin Linux kernel |
| | | * |
| | | * Version: 1.0.0(11/17/2021~) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "11/17/2021 02:46:18 PM" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <unistd.h> |
| | | #include <errno.h> |
| | | #include <string.h> |
| | | #include <stdlib.h> |
| | | #include <unistd.h> |
| | | #include <fcntl.h> |
| | | #include <libgen.h> |
| | | #include <getopt.h> |
| | | #include <sys/types.h> |
| | | #include <sys/ioctl.h> |
| | | #include <linux/input.h> |
| | | #include <linux/kd.h> |
| | | #include <linux/keyboard.h> |
| | | |
| | | #if 0 /* Just for comment here, Reference to linux-3.3/include/linux/input.h */ |
| | | struct input_event |
| | | { |
| | | struct timeval time; |
| | | __u16 type; /* 0x00:EV_SYN 0x01:EV_KEY 0x04:EV_MSC 0x11:EV_LED*/ |
| | | __u16 code; /* key value, which key */ |
| | | __s32 value; /* 1: Pressed 0:Not pressed 2:Always Pressed */ |
| | | }; |
| | | #endif |
| | | |
| | | #define EV_RELEASED 0 |
| | | #define EV_PRESSED 1 |
| | | |
| | | #define BUTTON_CNT 10 |
| | | |
| | | /* 在C语言编程中,函数应该先定义再使用,如果函数的定义在函数调用后面,应该前向声明。*/ |
| | | void usage(char *name); |
| | | |
| | | void display_button_event(struct input_event *ev, int cnt); |
| | | |
| | | int main(int argc, char **argv) |
| | | { |
| | | char *kbd_dev = "/dev/input/event1"; //默认监听按键设备; |
| | | char kbd_name[256] = "Unknown"; //用于保存获取到的设备名称 |
| | | int kbd_fd = -1; //open()打开文件的文件描述符 |
| | | int rv=0; // 函数返回值,默认返回0; |
| | | int opt; // getopt_long 解析命令行参数返回值; |
| | | int size = sizeof (struct input_event); |
| | | fd_set rds; //用于监听的事件的集合 |
| | | |
| | | struct input_event ev[BUTTON_CNT]; |
| | | |
| | | /* getopt_long参数函数第四个参数的定义,二维数组,每个成员由四个元素组成 */ |
| | | struct option long_options[] = { |
| | | /* { 参数名称,是否带参数,flags指针(NULL时将val的数值从getopt_long的返回值返回出去), |
| | | 函数找到该选项时的返回值(字符)} |
| | | */ |
| | | {"device", required_argument, NULL, 'd'}, |
| | | {"help", no_argument, NULL, 'h'}, |
| | | {NULL, 0, NULL, 0} |
| | | }; |
| | | |
| | | //获取命令行参数的解析返回值 |
| | | while ((opt = getopt_long(argc, argv, "d:h", long_options, NULL)) != -1) |
| | | { |
| | | switch (opt) |
| | | { |
| | | case 'd': |
| | | kbd_dev = optarg; |
| | | break; |
| | | |
| | | case 'h': |
| | | usage(argv[0]); |
| | | return 0; |
| | | |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if(NULL == kbd_dev) |
| | | { |
| | | /* 命令行argv[0]是输入的命令,如 ./keypad */ |
| | | usage(argv[0]); |
| | | return -1; |
| | | } |
| | | |
| | | /* 获取uid 建议以root权限运行确保可以正常运行 */ |
| | | if ((getuid ()) != 0) |
| | | printf ("You are not root! This may not work...\n"); |
| | | |
| | | /* 打开按键对应的设备节点,如果错误则返回负数 */ |
| | | if ((kbd_fd = open(kbd_dev, O_RDONLY)) < 0) |
| | | { |
| | | printf("Open %s failure: %s", kbd_dev, strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | /* 使用ioctl获取 /dev/input/event*对应的设备名字 */ |
| | | ioctl (kbd_fd, EVIOCGNAME (sizeof (kbd_name)), kbd_name); |
| | | printf ("Monitor input device %s (%s) event on poll mode:\n", kbd_dev, kbd_name); |
| | | |
| | | /* 循环使用 select() 多路复用监听按键事件 */ |
| | | while (1) |
| | | { |
| | | FD_ZERO(&rds); /* 清空 select() 的读事件集合 */ |
| | | FD_SET(kbd_fd, &rds); /* 将按键设备的文件描述符加入到读事件集合中*/ |
| | | |
| | | /* 使用select开启监听并等待多个描述符发生变化,第一个参数最大描述符+1, |
| | | 2、3、4参数分别是要监听读、写、异常三个事件的文军描述符集合; |
| | | 最后一个参数是超时时间(NULL-->永不超时,会一直阻塞住) |
| | | |
| | | 如果按键没有按下,则程序一直阻塞在这里。一旦按键按下,则按键设备有数据 |
| | | 可读,此时函数将返回。 |
| | | */ |
| | | rv = select(kbd_fd + 1, &rds, NULL, NULL, NULL); |
| | | if (rv < 0) |
| | | { |
| | | printf("Select() system call failure: %s\n", strerror(errno)); |
| | | goto CleanUp; |
| | | } |
| | | else if (FD_ISSET(kbd_fd, &rds)) /* 是按键设备发生了事件 */ |
| | | { |
| | | //read读取input设备的数据包,数据包为input_event结构体类型。 |
| | | if ((rv = read (kbd_fd, ev, size*BUTTON_CNT )) < size) |
| | | { |
| | | printf("Reading data from kbd_fd failure: %s\n", strerror(errno)); |
| | | break; |
| | | } |
| | | else |
| | | { |
| | | display_button_event(ev, rv/size); |
| | | } |
| | | } |
| | | } |
| | | |
| | | CleanUp: |
| | | close(kbd_fd); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /* 该函数用来打印程序的使用方法 */ |
| | | void usage(char *name) |
| | | { |
| | | char *progname = NULL; |
| | | char *ptr = NULL; |
| | | |
| | | /* 字符串拷贝函数,该函数内部将调用malloc()来动态分配内存,然后将$name |
| | | 字符串内容拷贝到malloc分配的内存中,这样使用完之后需要free释放内存. */ |
| | | ptr = strdup(name); |
| | | progname = basename(ptr); //去除该可执行文件的路径名,获取其自身名称(即keypad) |
| | | |
| | | printf("Usage: %s [-p] -d <device>\n", progname); |
| | | printf(" -d[device ] button device name\n"); |
| | | printf(" -p[poll ] Use poll mode, or default use infinit loop.\n"); |
| | | printf(" -h[help ] Display this help information\n"); |
| | | |
| | | free(ptr); //和strdup对应,释放该内存 |
| | | return; |
| | | } |
| | | |
| | | /* 该函数用来解析按键设备上报的数据,并答应按键按下的相关信息 */ |
| | | void display_button_event(struct input_event *ev, int cnt) |
| | | { |
| | | int i; |
| | | static struct timeval pressed_time; //该变量用来存放按键按下的时间,注意static的使用。 |
| | | struct timeval duration_time; //该变量用来存放按键按下持续时间 |
| | | |
| | | for(i=0; i<cnt; i++) |
| | | { |
| | | /* 当上报的时间type为EV_KEY时候并且,value值为1或0 (1为按下,0为释放) */ |
| | | if(EV_KEY==ev[i].type && EV_PRESSED==ev[i].value) |
| | | { |
| | | pressed_time = ev[i].time; |
| | | printf("Keypad[%d] pressed time: %ld.%ld\n", |
| | | ev[i].code, pressed_time.tv_sec, pressed_time.tv_usec); |
| | | } |
| | | if(EV_KEY==ev[i].type && EV_RELEASED==ev[i].value) |
| | | { |
| | | /* 计算时间差函数 将第一个参数减去第二个参数的值的结果 放到第三个参数之中 */ |
| | | timersub(&ev[i].time, &pressed_time, &duration_time); |
| | | printf("keypad[%d] released time: %ld.%ld\n", |
| | | ev[i].code, ev[i].time.tv_sec, ev[i].time.tv_usec); |
| | | printf("keypad[%d] duration time: %ld.%ld\n", |
| | | ev[i].code, duration_time.tv_sec, duration_time.tv_usec); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2024 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: led.c |
| | | * Description: This file is used to control RGB 3-colors LED |
| | | * |
| | | * |
| | | * Pin connection: |
| | | * RGB Led Module IGKBoard |
| | | * R <-----> #Pin33 |
| | | * G <-----> #Pin35 |
| | | * B <-----> #Pin37 |
| | | * GND <-----> GND |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <unistd.h> |
| | | #include <fcntl.h> |
| | | #include <dirent.h> |
| | | #include <string.h> |
| | | #include <time.h> |
| | | #include <errno.h> |
| | | #include <signal.h> |
| | | |
| | | #include <gpiod.h> |
| | | |
| | | #define DELAY 300 |
| | | |
| | | #define ON 1 |
| | | #define OFF 0 |
| | | |
| | | /* Three LEDs number */ |
| | | enum |
| | | { |
| | | LED_R = 0, |
| | | LED_G, |
| | | LED_B, |
| | | LEDCNT, |
| | | }; |
| | | |
| | | enum |
| | | { |
| | | ACTIVE_HIGH, /* High level will turn led on */ |
| | | ACTIVE_LOW, /* Low level will turn led on */ |
| | | }; |
| | | |
| | | /* Three LEDs hardware information */ |
| | | typedef struct led_s |
| | | { |
| | | const char *name; /* RGB 3-color LED name */ |
| | | int chip_num; /* RGB 3-color LED connect chip */ |
| | | int gpio_num; /* RGB 3-color LED connect line */ |
| | | int active; /* RGB 3-color LED active level */ |
| | | struct gpiod_line_request *request; /* libgpiod gpio request handler */ |
| | | } led_t; |
| | | |
| | | static led_t leds_info[LEDCNT] = |
| | | { |
| | | {"red", 0, 23, ACTIVE_HIGH, NULL }, /* GPIO1_IO23 on chip0 line 23, active high */ |
| | | {"green", 4, 1, ACTIVE_HIGH, NULL }, /* GPIO5_IO01 on chip4 line 1, active high */ |
| | | {"blue", 4, 8, ACTIVE_HIGH, NULL }, /* GPIO5_IO08 on chip4 line 8, active high */ |
| | | }; |
| | | |
| | | /* Three LEDs API context */ |
| | | typedef struct leds_s |
| | | { |
| | | led_t *leds; /* led pointer to leds_info */ |
| | | int count; /* led count */ |
| | | } leds_t; |
| | | |
| | | |
| | | /* function declaration */ |
| | | int init_led(leds_t *leds); |
| | | int term_led(leds_t *leds); |
| | | int turn_led(leds_t *leds, int which, int cmd); |
| | | static inline void msleep(unsigned long ms); |
| | | |
| | | |
| | | int g_stop = 0; |
| | | |
| | | void sig_handler(int signum) |
| | | { |
| | | switch( signum ) |
| | | { |
| | | case SIGINT: |
| | | case SIGTERM: |
| | | g_stop = 1; |
| | | |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | return ; |
| | | } |
| | | |
| | | int main(int argc, char *argv[]) |
| | | { |
| | | int rv; |
| | | leds_t leds = |
| | | { |
| | | .leds = leds_info, |
| | | .count = LEDCNT, |
| | | }; |
| | | |
| | | if( (rv=init_led(&leds)) < 0 ) |
| | | { |
| | | printf("initial leds gpio failure, rv=%d\n", rv); |
| | | return 1; |
| | | } |
| | | printf("initial RGB Led gpios okay\n"); |
| | | |
| | | signal(SIGINT, sig_handler); |
| | | signal(SIGTERM, sig_handler); |
| | | |
| | | while( !g_stop ) |
| | | { |
| | | turn_led(&leds, LED_R, ON); |
| | | msleep(DELAY); |
| | | turn_led(&leds, LED_R, OFF); |
| | | msleep(DELAY); |
| | | |
| | | turn_led(&leds, LED_G, ON); |
| | | msleep(DELAY); |
| | | turn_led(&leds, LED_G, OFF); |
| | | msleep(DELAY); |
| | | |
| | | turn_led(&leds, LED_B, ON); |
| | | msleep(DELAY); |
| | | turn_led(&leds, LED_B, OFF); |
| | | msleep(DELAY); |
| | | } |
| | | |
| | | term_led(&leds); |
| | | return 0; |
| | | } |
| | | |
| | | int term_led(leds_t *leds) |
| | | { |
| | | int i; |
| | | led_t *led; |
| | | |
| | | printf("terminate RGB Led gpios\n"); |
| | | |
| | | if( !leds ) |
| | | { |
| | | printf("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | for(i=0; i<leds->count; i++) |
| | | { |
| | | led = &leds->leds[i]; |
| | | |
| | | if( led->request ) |
| | | { |
| | | turn_led(leds, i, OFF); |
| | | gpiod_line_request_release(led->request); |
| | | } |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | int init_led(leds_t *leds) |
| | | { |
| | | led_t *led; |
| | | int i, rv = 0; |
| | | char chip_dev[32]; |
| | | struct gpiod_chip *chip; /* gpio chip */ |
| | | struct gpiod_line_settings *settings; /* gpio direction, bias, active_low, value */ |
| | | struct gpiod_line_config *line_cfg; /* gpio line */ |
| | | struct gpiod_request_config *req_cfg; /* gpio consumer, it can be NULL */ |
| | | |
| | | |
| | | if( !leds ) |
| | | { |
| | | printf("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | |
| | | /* defined in libgpiod-2.0/lib/line-settings.c: |
| | | |
| | | struct gpiod_line_settings { |
| | | enum gpiod_line_direction direction; |
| | | enum gpiod_line_edge edge_detection; |
| | | enum gpiod_line_drive drive; |
| | | enum gpiod_line_bias bias; |
| | | bool active_low; |
| | | enum gpiod_line_clock event_clock; |
| | | long debounce_period_us; |
| | | enum gpiod_line_value output_value; |
| | | }; |
| | | */ |
| | | settings = gpiod_line_settings_new(); |
| | | if (!settings) |
| | | { |
| | | printf("unable to allocate line settings\n"); |
| | | rv = -2; |
| | | goto cleanup; |
| | | } |
| | | |
| | | /* defined in libgpiod-2.0/lib/line-config.c |
| | | |
| | | struct gpiod_line_config { |
| | | struct per_line_config line_configs[LINES_MAX]; |
| | | size_t num_configs; |
| | | enum gpiod_line_value output_values[LINES_MAX]; |
| | | size_t num_output_values; |
| | | struct settings_node *sref_list; |
| | | }; |
| | | */ |
| | | |
| | | line_cfg = gpiod_line_config_new(); |
| | | if (!line_cfg) |
| | | { |
| | | printf("unable to allocate the line config structure"); |
| | | rv = -2; |
| | | goto cleanup; |
| | | } |
| | | |
| | | |
| | | /* defined in libgpiod-2.0/lib/request-config.c: |
| | | |
| | | struct gpiod_request_config { |
| | | char consumer[GPIO_MAX_NAME_SIZE]; |
| | | size_t event_buffer_size; |
| | | }; |
| | | */ |
| | | req_cfg = gpiod_request_config_new(); |
| | | if (!req_cfg) |
| | | { |
| | | printf("unable to allocate the request config structure"); |
| | | rv = -2; |
| | | goto cleanup; |
| | | } |
| | | |
| | | for(i=0; i<leds->count; i++) |
| | | { |
| | | led = &leds->leds[i]; |
| | | |
| | | snprintf(chip_dev, sizeof(chip_dev), "/dev/gpiochip%d", led->chip_num); |
| | | chip = gpiod_chip_open(chip_dev); |
| | | if( !chip ) |
| | | { |
| | | printf("open gpiochip failure, maybe you need running as root\n"); |
| | | rv = -3; |
| | | goto cleanup; |
| | | } |
| | | |
| | | /* Set as output direction, active low and default level as inactive */ |
| | | gpiod_line_settings_reset(settings); |
| | | gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT); |
| | | gpiod_line_settings_set_active_low(settings, led->active); |
| | | gpiod_line_settings_set_output_value(settings, GPIOD_LINE_VALUE_INACTIVE); |
| | | |
| | | /* set gpio line */ |
| | | gpiod_line_config_reset(line_cfg); |
| | | gpiod_line_config_add_line_settings(line_cfg, &led->gpio_num, 1, settings); |
| | | |
| | | /* Can be NULL for default settings. */ |
| | | gpiod_request_config_set_consumer(req_cfg, led->name); |
| | | |
| | | /* Request a set of lines for exclusive usage. */ |
| | | led->request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); |
| | | |
| | | gpiod_chip_close(chip); |
| | | //printf("request %5s led[%d] for gpio output okay\n", led->name, led->gpio); |
| | | } |
| | | |
| | | cleanup: |
| | | |
| | | if( rv< 0 ) |
| | | term_led(leds); |
| | | |
| | | if( line_cfg ) |
| | | gpiod_line_config_free(line_cfg); |
| | | |
| | | if( req_cfg ) |
| | | gpiod_request_config_free(req_cfg); |
| | | |
| | | if( settings ) |
| | | gpiod_line_settings_free(settings); |
| | | |
| | | return rv; |
| | | } |
| | | |
| | | int turn_led(leds_t *leds, int which, int cmd) |
| | | { |
| | | led_t *led; |
| | | int rv = 0; |
| | | int value = 0; |
| | | |
| | | if( !leds || which<0 || which>=leds->count ) |
| | | { |
| | | printf("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | led = &leds->leds[which]; |
| | | |
| | | value = OFF==cmd ? GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE; |
| | | |
| | | gpiod_line_request_set_value(led->request, led->gpio_num, value); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | static inline void msleep(unsigned long ms) |
| | | { |
| | | struct timespec cSleep; |
| | | unsigned long ulTmp; |
| | | |
| | | cSleep.tv_sec = ms / 1000; |
| | | if (cSleep.tv_sec == 0) |
| | | { |
| | | ulTmp = ms * 10000; |
| | | cSleep.tv_nsec = ulTmp * 100; |
| | | } |
| | | else |
| | | { |
| | | cSleep.tv_nsec = 0; |
| | | } |
| | | |
| | | nanosleep(&cSleep, 0); |
| | | |
| | | return ; |
| | | } |
New file |
| | |
| | | #!/bin/bash |
| | | |
| | | # library name and version |
| | | # Official: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git |
| | | LIB_NAME=libgpiod-2.0 |
| | | PACK_SUFIX=tar.gz |
| | | |
| | | # Cross compiler for cross compile on Linux server |
| | | CROSS_COMPILE=arm-linux-gnueabihf- |
| | | |
| | | # this project absolute path |
| | | PRJ_PATH=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd) |
| | | |
| | | # binaries install path |
| | | PREFIX_PATH=$PRJ_PATH/install |
| | | |
| | | # check installed or not file |
| | | INST_FILE=$PREFIX_PATH/lib/libgpiod.so |
| | | |
| | | #+-------------------------+ |
| | | #| Shell script functions | |
| | | #+-------------------------+ |
| | | |
| | | function pr_error() { |
| | | echo -e "\033[40;31m $1 \033[0m" |
| | | } |
| | | |
| | | function pr_warn() { |
| | | echo -e "\033[40;33m $1 \033[0m" |
| | | } |
| | | |
| | | function pr_info() { |
| | | echo -e "\033[40;32m $1 \033[0m" |
| | | } |
| | | |
| | | function check_result() |
| | | { |
| | | if [ $? != 0 ] ; then |
| | | pr_error $1 |
| | | fi |
| | | } |
| | | |
| | | function do_export() |
| | | { |
| | | pr_warn "set cross(${CROSS_COMPILE})" |
| | | |
| | | # export cross toolchain |
| | | export CC=${CROSS_COMPILE}gcc |
| | | export CXX=${CROSS_COMPILE}g++ |
| | | export AS=${CROSS_COMPILE}as |
| | | export AR=${CROSS_COMPILE}ar |
| | | export LD=${CROSS_COMPILE}ld |
| | | export NM=${CROSS_COMPILE}nm |
| | | export RANLIB=${CROSS_COMPILE}ranlib |
| | | export OBJDUMP=${CROSS_COMPILE}objdump |
| | | export STRIP=${CROSS_COMPILE}strip |
| | | |
| | | # export cross configure |
| | | export CONFIG_CROSS=" --build=i686-pc-linux --host=arm-linux " |
| | | |
| | | # Clear LDFLAGS and CFLAGS |
| | | export LDFLAGS= |
| | | export CFLAGS= |
| | | } |
| | | |
| | | function do_fetch() |
| | | { |
| | | if [ -e ${INST_FILE} ] ; then |
| | | pr_warn "$LIB_NAME compile and installed alredy" |
| | | exit ; |
| | | fi |
| | | |
| | | if [ -d $LIB_NAME ] ; then |
| | | pr_warn "$LIB_NAME fetch already" |
| | | return ; |
| | | fi |
| | | |
| | | if [ ! -f ${LIB_NAME}.${PACK_SUFIX} ] ; then |
| | | wget https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/snapshot/${LIB_NAME}.${PACK_SUFIX} |
| | | check_result "ERROR: download ${LIB_NAME} failure" |
| | | fi |
| | | |
| | | pr_warn "$LIB_NAME download already, decompress it now" |
| | | tar -xzf ${LIB_NAME}.${PACK_SUFIX} |
| | | } |
| | | |
| | | function do_build() |
| | | { |
| | | cd $LIB_NAME |
| | | |
| | | ./autogen.sh |
| | | |
| | | do_export |
| | | |
| | | echo "ac_cv_func_malloc_0_nonnull=yes" > arm-linux.cache |
| | | ./configure --prefix=${PREFIX_PATH} ${CONFIG_CROSS} --enable-static \ |
| | | --cache-file=arm-linux.cache --enable-tools |
| | | check_result "ERROR: configure ${LIB_NAME} failure" |
| | | |
| | | make -j ${JOBS} && make install |
| | | check_result "ERROR: compile ${LIB_NAME} failure" |
| | | } |
| | | |
| | | function do_clean() |
| | | { |
| | | rm -rf ${LIB_NAME}* |
| | | rm -rf install |
| | | } |
| | | |
| | | if [[ $# == 1 && $1 == -c ]] ;then |
| | | pr_warn "start clean $LIB_NAME" |
| | | do_clean |
| | | exit; |
| | | fi |
| | | |
| | | do_fetch |
| | | |
| | | do_build |
New file |
| | |
| | | |
| | | all: update_cross |
| | | @./build.sh |
| | | |
| | | clean: |
| | | @./build.sh -c |
| | | |
| | | update_cross: |
| | | ifdef CROSS_COMPILE |
| | | sed -i 's|^CROSS_COMPILE=.*|CROSS_COMPILE=${CROSS_COMPILE}|g' build.sh |
| | | endif |
New file |
| | |
| | | |
| | | # Cross compiler |
| | | CROSS_COMPILE=arm-linux-gnueabihf- |
| | | CC=${CROSS_COMPILE}gcc |
| | | AR=${CROSS_COMPILE}ar |
| | | |
| | | # libgpiod compile install path |
| | | LIBGPIOD_PATH=libgpiod/install |
| | | |
| | | # compile flags and link flags |
| | | CFLAGS+=-I ${LIBGPIOD_PATH}/include |
| | | LDFLAGS+=-L ${LIBGPIOD_PATH}/lib -lgpiod |
| | | |
| | | INSTALL_BINS=can_test |
| | | |
| | | all: libs |
| | | ${CC} hello.c -o hello |
| | | ${CC} ${CFLAGS} leds.c -o leds ${LDFLAGS} |
| | | ${CC} keypad.c -o keypad |
| | | ${CC} ds18b20.c -o ds18b20 |
| | | ${CC} pwm.c -o pwm |
| | | ${CC} pwm_play.c -o pwm_play |
| | | ${CC} sht20_fops.c -o sht20_fops |
| | | ${CC} sht20_ioctl.c -o sht20_ioctl |
| | | ${CC} spi_test.c -o spi_test |
| | | ${CC} ttyS_test.c -o ttyS_test -lpthread |
| | | ${CC} can_test.c -o can_test |
| | | @make install |
| | | |
| | | libs: |
| | | make -C libgpiod CROSS_COMPILE=${CROSS_COMPILE} |
| | | |
| | | clean: |
| | | @rm -f hello |
| | | @rm -f leds |
| | | @rm -f keypad |
| | | @rm -f ds18b20 |
| | | @rm -f pwm |
| | | @rm -f pwm_play |
| | | @rm -f sht20_fops |
| | | @rm -f sht20_ioctl |
| | | @rm -f spi_test |
| | | @rm -f ttyS_test |
| | | @rm -f can_test |
| | | |
| | | distclean: clean |
| | | make clean -C libgpiod |
| | | |
| | | install: |
| | | cp ${INSTALL_BINS} /tftp |
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2021 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: pwm.c |
| | | * Description: This file is used to control PWM buzzer/Led |
| | | * |
| | | * Pin connection: |
| | | * PWM Module IGKBoard |
| | | * VCC <-----> 5V |
| | | * Led <-----> #Pin28(PWM8) |
| | | * GND <-----> GND |
| | | * |
| | | * |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <unistd.h> |
| | | #include <fcntl.h> |
| | | #include <dirent.h> |
| | | #include <string.h> |
| | | #include <time.h> |
| | | #include <errno.h> |
| | | #include <signal.h> |
| | | #include <getopt.h> |
| | | #include <libgen.h> |
| | | |
| | | #define PWM_SYS_PATH "/sys/class/pwm/pwmchip0" |
| | | |
| | | #define ENABLE 1 |
| | | #define DISABLE 0 |
| | | |
| | | |
| | | typedef struct pwm_s |
| | | { |
| | | char pwm_path[64]; /* PWM path, such as /sys/class/pwm/pwmchip0 */ |
| | | char chn_path[64]; /* channel path, such as /sys/class/pwm/pwmchip0/pwm0 */ |
| | | int pwm_num; /* pwm number */ |
| | | int chn_num; /* channel number */ |
| | | } pwm_t; |
| | | |
| | | |
| | | int init_pwm(pwm_t *pwm, int pwm_num, int chn_num); |
| | | int conf_pwm(pwm_t *pwm, int freq, int duty); |
| | | int turn_pwm(pwm_t *pwm, int status); |
| | | int term_pwm(pwm_t *pwm); |
| | | |
| | | static inline void msleep(unsigned long ms); |
| | | |
| | | static inline void banner(const char *progname) |
| | | { |
| | | printf("%s program Version v1.0.0\n", progname); |
| | | printf("Copyright (C) 2023 LingYun IoT System Studio.\n"); |
| | | } |
| | | |
| | | static void program_usage(const char *progname) |
| | | { |
| | | |
| | | printf("Usage: %s [OPTION]...\n", progname); |
| | | printf(" This is pwm control program. \n"); |
| | | |
| | | printf(" -p[pwm ] Specify PWM chip, such as 1 for pwmchip1\n"); |
| | | printf(" -c[channel ] Specify PWM channel, such as 0\n"); |
| | | printf(" -f[freq ] Specify PWM frequency, default 2500(Hz)\n"); |
| | | printf(" -d[duty ] Specify PWM duty, default 50(50%%)\n"); |
| | | printf(" -s[status ] Specify PWM status: 1->on(default), 0->off\n"); |
| | | printf(" -h[help ] Display this help information\n"); |
| | | printf(" -v[version ] Display the program version\n"); |
| | | printf("\n"); |
| | | |
| | | printf("Example buzzer : %s -p 1 -c 0 -f 10000 -d 50 -s 1\n", progname); |
| | | printf("Example Led : %s -p 1 -c 1 -f 100 -d 50 -s 1\n", progname); |
| | | printf("Example disable: %s -p 1 -c 0 -s 0\n", progname); |
| | | |
| | | printf("\n"); |
| | | banner(progname); |
| | | return; |
| | | } |
| | | |
| | | int main(int argc, char **argv) |
| | | { |
| | | int rv; |
| | | char *progname=NULL; |
| | | int chn_num = -1; |
| | | int pwm_num = -1; |
| | | int freq = 2500; |
| | | int duty = 50; |
| | | int status = ENABLE; |
| | | pwm_t pwm; |
| | | |
| | | struct option long_options[] = { |
| | | {"pwm", required_argument, NULL, 'p'}, |
| | | {"channel", required_argument, NULL, 'c'}, |
| | | {"freq", required_argument, NULL, 'f'}, |
| | | {"duty", required_argument, NULL, 'd'}, |
| | | {"status", required_argument, NULL, 's'}, |
| | | {"version", no_argument, NULL, 'v'}, |
| | | {"help", no_argument, NULL, 'h'}, |
| | | {NULL, 0, NULL, 0} |
| | | }; |
| | | |
| | | progname = basename(argv[0]); |
| | | |
| | | /* Parser the command line parameters */ |
| | | while ((rv = getopt_long(argc, argv, "p:c:f:d:s:vh", long_options, NULL)) != -1) |
| | | { |
| | | switch (rv) |
| | | { |
| | | case 'p': /* Set pwm chip, such as 1 for pwmchip1 */ |
| | | pwm_num = atoi(optarg); |
| | | break; |
| | | |
| | | case 'c': /* Set pwm channel, such as 0,1 */ |
| | | chn_num = atoi(optarg); |
| | | break; |
| | | |
| | | case 'f': /* Set pwm frequency */ |
| | | freq = atoi(optarg); |
| | | break; |
| | | |
| | | case 'd': /* Set pwm duty cycle */ |
| | | duty = atoi(optarg); |
| | | break; |
| | | |
| | | case 's': /* Set pwm status, 0 for disable and 1 for enable */ |
| | | status = atoi(optarg); |
| | | break; |
| | | |
| | | case 'v': /* Get software version */ |
| | | banner(progname); |
| | | return EXIT_SUCCESS; |
| | | |
| | | case 'h': /* Get help information */ |
| | | program_usage(progname); |
| | | return 0; |
| | | |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if( pwm_num<0 || chn_num<0 ) |
| | | { |
| | | program_usage(progname); |
| | | return 0; |
| | | } |
| | | |
| | | init_pwm(&pwm, pwm_num, chn_num); |
| | | |
| | | |
| | | if( status ) |
| | | { |
| | | if( (rv=conf_pwm(&pwm, freq, duty)) < 0 ) |
| | | { |
| | | printf("Configure PWM failure, rv=%d\n", rv); |
| | | return 1; |
| | | } |
| | | |
| | | turn_pwm(&pwm, ENABLE); |
| | | } |
| | | else |
| | | { |
| | | term_pwm(&pwm); |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int init_pwm(pwm_t *pwm, int pwm_num, int chn_num) |
| | | { |
| | | if( !pwm || pwm_num<0 || chn_num<0 ) |
| | | { |
| | | printf("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | snprintf(pwm->pwm_path, sizeof(pwm->pwm_path), "/sys/class/pwm/pwmchip%d", pwm_num); |
| | | snprintf(pwm->chn_path, sizeof(pwm->chn_path), "/sys/class/pwm/pwmchip%d/pwm%d", pwm_num, chn_num); |
| | | pwm->pwm_num = pwm_num; |
| | | pwm->chn_num = chn_num; |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /* check PWM $channel export or not */ |
| | | int check_pwm(pwm_t *pwm) |
| | | { |
| | | /* check /sys/class/pwm/pwmchipX/pwmY exist or not */ |
| | | return access(pwm->chn_path, F_OK) ? 0 : 1; |
| | | } |
| | | |
| | | /* export($export=1)/unexport($export=0) PWM $channel */ |
| | | int export_pwm(pwm_t *pwm, int export) |
| | | { |
| | | int fd; |
| | | char buf[32]; |
| | | char path[256]; |
| | | |
| | | if( export && check_pwm(pwm) ) |
| | | return 0; /* export already when export */ |
| | | else if( !export && !check_pwm(pwm) ) |
| | | return 0; /* unexport already when unexport */ |
| | | |
| | | /* export PWM channel by echo Y > /sys/class/pwm/pwmchipX/export */ |
| | | snprintf(path, sizeof(path), "%s/%s", pwm->pwm_path, export?"export":"unexport"); |
| | | if( (fd=open(path, O_WRONLY)) < 0 ) |
| | | { |
| | | printf("open '%s' failed: %s\n", path, strerror(errno)); |
| | | return -3; |
| | | } |
| | | |
| | | snprintf(buf, sizeof(buf), "%d", pwm->chn_num); |
| | | write(fd, buf, strlen(buf)); |
| | | |
| | | msleep(100); |
| | | |
| | | if( export && check_pwm(pwm) ) |
| | | return 0; /* export already when export */ |
| | | else if( !export && !check_pwm(pwm) ) |
| | | return 0; /* unexport already when unexport */ |
| | | |
| | | return -4; |
| | | } |
| | | |
| | | /* set PWM $channel */ |
| | | int set_pwm(pwm_t *pwm, int freq, int duty) |
| | | { |
| | | int fd; |
| | | char buf[32]; |
| | | char path[256]; |
| | | int period; |
| | | int duty_cycle; |
| | | |
| | | if( !check_pwm(pwm) ) |
| | | return -2; |
| | | |
| | | /* open PWM period file and write period in ns */ |
| | | snprintf(path, sizeof(path), "%s/period", pwm->chn_path); |
| | | if( (fd=open(path, O_WRONLY)) < 0 ) |
| | | { |
| | | printf("open '%s' failed: %s\n", path, strerror(errno)); |
| | | return -3; |
| | | } |
| | | period = 1000000000/freq; |
| | | snprintf(buf, sizeof(buf), "%d", period); |
| | | write(fd, buf, strlen(buf)); |
| | | |
| | | /* open PWM duty_cycle file and write duty */ |
| | | snprintf(path, sizeof(path), "%s/duty_cycle", pwm->chn_path); |
| | | if( (fd=open(path, O_WRONLY)) < 0 ) |
| | | { |
| | | printf("open '%s' failed: %s\n", path, strerror(errno)); |
| | | return -3; |
| | | } |
| | | duty_cycle = (period*duty) / 100; |
| | | snprintf(buf, sizeof(buf), "%d", duty_cycle); |
| | | write(fd, buf, strlen(buf)); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int conf_pwm(pwm_t *pwm, int freq, int duty) |
| | | { |
| | | int rv; |
| | | char buf[32]; |
| | | char path[256]; |
| | | |
| | | if( (rv=export_pwm(pwm, 1)) ) |
| | | { |
| | | printf("export PWM channel[%d] failed, rv=%d\n", pwm->chn_num, rv); |
| | | return rv; |
| | | } |
| | | |
| | | if( (rv=set_pwm(pwm, freq, duty)) ) |
| | | { |
| | | printf("config PWM channel[%d] failed, rv=%d\n", pwm->chn_num, rv); |
| | | return rv; |
| | | } |
| | | |
| | | printf("config pwm%d channel%d with freq[%d] duty[%d] okay\n", pwm->pwm_num, pwm->chn_num, freq, duty); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int turn_pwm(pwm_t *pwm, int status) |
| | | { |
| | | int fd; |
| | | char buf[32]; |
| | | char path[256]; |
| | | |
| | | if( !check_pwm(pwm) ) |
| | | return -1; |
| | | |
| | | /* open PWM enable file and enable(1)/disable(0) it */ |
| | | snprintf(path, sizeof(path), "%s/enable", pwm->chn_path); |
| | | if( (fd=open(path, O_WRONLY)) < 0 ) |
| | | { |
| | | printf("open '%s' failed: %s\n", path, strerror(errno)); |
| | | return -3; |
| | | } |
| | | snprintf(buf, sizeof(buf), "%d", status?1:0); |
| | | write(fd, buf, strlen(buf)); |
| | | |
| | | printf("pwm[%d] channel[%d]%s\n", pwm->pwm_num, pwm->chn_num, status?"enable":"disable"); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int term_pwm(pwm_t *pwm) |
| | | { |
| | | if( !check_pwm(pwm) ) |
| | | return 0; |
| | | |
| | | turn_pwm(pwm, DISABLE); |
| | | export_pwm(pwm, 0); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | static inline void msleep(unsigned long ms) |
| | | { |
| | | struct timespec cSleep; |
| | | unsigned long ulTmp; |
| | | |
| | | cSleep.tv_sec = ms / 1000; |
| | | if (cSleep.tv_sec == 0) |
| | | { |
| | | ulTmp = ms * 10000; |
| | | cSleep.tv_nsec = ulTmp * 100; |
| | | } |
| | | else |
| | | { |
| | | cSleep.tv_nsec = 0; |
| | | } |
| | | |
| | | nanosleep(&cSleep, 0); |
| | | |
| | | return ; |
| | | } |
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2021 Guo Wenxue<Email:guowenxue@gmail.com QQ:281143292> |
| | | * All rights reserved. |
| | | * |
| | | * Filename: pwm_music.c |
| | | * Description: This file used to test PWM music |
| | | * |
| | | * Version: 1.0.0(10/21/2022~) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "10/21/2022 17:46:18 PM" |
| | | * |
| | | ********************************************************************************/ |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <sys/types.h> |
| | | #include <sys/stat.h> |
| | | #include <fcntl.h> |
| | | #include <time.h> |
| | | #include <unistd.h> |
| | | #include <string.h> |
| | | |
| | | #define PWM_CHIP 1 /* buzzer on pwmchip1 */ |
| | | |
| | | typedef struct pwm_note_s |
| | | { |
| | | unsigned int msec; //持续时间,毫秒 |
| | | unsigned int freq;//频率 |
| | | unsigned char duty;//相对占空比,百分比 * 100 |
| | | }pwm_note_t; |
| | | |
| | | /* 使用宏确定使用中音还是高、低音 */ |
| | | #define CX CM |
| | | |
| | | /* 低、中、高音频率*/ |
| | | static const unsigned short CL[8] = {0, 262, 294, 330, 349, 392, 440, 494}; |
| | | static const unsigned short CM[8] = {0, 523, 587, 659, 698, 784, 880, 988}; |
| | | static const unsigned short CH[8] = {0, 1046, 1175, 1318, 1397, 1568, 1760, 1976}; |
| | | |
| | | /* 小星星曲子*/ |
| | | static unsigned short melody[] = { |
| | | CX[1], CX[1], CX[5], CX[5], CX[6], CX[6], CX[5], CX[0], |
| | | CX[4], CX[4], CX[3], CX[3], CX[2], CX[2], CX[1], CX[0], |
| | | CX[5], CX[5], CX[4], CX[4], CX[3], CX[3], CX[2], CX[0], |
| | | CX[5], CX[5], CX[4], CX[4], CX[3], CX[3], CX[2], CX[0], |
| | | }; |
| | | |
| | | static char pwm_path[64]; /* pwm file path buffer */ |
| | | |
| | | static int pwm_export(int chip); |
| | | static int pwm_config(const char *attr, const char *val); |
| | | static int pwm_ring(pwm_note_t *note); |
| | | static inline void msleep(unsigned long ms); |
| | | |
| | | int main(int argc, char *argv[]) |
| | | { |
| | | pwm_note_t note; |
| | | int i; |
| | | |
| | | if( pwm_export(PWM_CHIP) < 0 ) |
| | | return 1; |
| | | |
| | | pwm_config("enable", "1"); |
| | | |
| | | for(i=0; i<sizeof(melody)/sizeof(melody[0]); i++) |
| | | { |
| | | if(melody[i] == 0) |
| | | { |
| | | note.duty = 0; |
| | | } |
| | | else |
| | | { |
| | | note.duty = 15; //越大音量越大 |
| | | note.freq = melody[i]; |
| | | } |
| | | note.msec = 300; |
| | | |
| | | pwm_ring(¬e); |
| | | } |
| | | |
| | | pwm_config("enable", "0"); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | static int pwm_export(int chip) |
| | | { |
| | | char file_path[100]; |
| | | int fd, rv=0; |
| | | |
| | | /* 导出pwm 首先确定最终导出的文件夹路径*/ |
| | | memset(pwm_path, 0, sizeof(pwm_path)); |
| | | snprintf(pwm_path, sizeof(pwm_path), "/sys/class/pwm/pwmchip%d/pwm0", PWM_CHIP); |
| | | |
| | | //如果pwm0 目录已经存在了, 则直接返回 |
| | | if ( !access(pwm_path, F_OK)) |
| | | return 0; |
| | | |
| | | //如果pwm0 目录不存在, 则开始导出 |
| | | |
| | | memset(file_path, 0, sizeof(file_path)); |
| | | snprintf(file_path, sizeof(file_path) , "/sys/class/pwm/pwmchip%d/export", PWM_CHIP); |
| | | |
| | | if ( (fd = open(file_path, O_WRONLY) < 0)) |
| | | { |
| | | printf("ERROR: open pwmchip%d error\n", PWM_CHIP); |
| | | return 1; |
| | | } |
| | | |
| | | if ( write(fd, "0", 1) < 0 ) |
| | | { |
| | | printf("write '0' to pwmchip%d/export error\n", PWM_CHIP); |
| | | rv = 2; |
| | | } |
| | | |
| | | close(fd); |
| | | return rv; |
| | | } |
| | | |
| | | /*pwm 配置函数 attr:属性文件名字 |
| | | * val:属性的值(字符串) |
| | | */ |
| | | static int pwm_config(const char *attr, const char *val) |
| | | { |
| | | char file_path[100]; |
| | | int fd; |
| | | |
| | | if( !attr || !val ) |
| | | { |
| | | printf("[%s] argument error\n", __FUNCTION__); |
| | | return -1; |
| | | } |
| | | |
| | | memset(file_path, 0, sizeof(file_path)); |
| | | snprintf(file_path, sizeof(file_path), "%s/%s", pwm_path, attr); |
| | | if( (fd=open(file_path, O_WRONLY)) < 0 ) |
| | | { |
| | | printf("[%s] open %s error\n", __FUNCTION__, file_path); |
| | | return fd; |
| | | } |
| | | |
| | | if ( write(fd, val, strlen(val)) < 0) { |
| | | printf("[%s] write %s to %s error\n", __FUNCTION__, val, file_path); |
| | | close(fd); |
| | | return -2; |
| | | } |
| | | |
| | | close(fd); //关闭文件 |
| | | return 0; |
| | | } |
| | | |
| | | /* pwm蜂鸣器响一次声音 */ |
| | | static int pwm_ring(pwm_note_t *note) |
| | | { |
| | | unsigned long period = 0; |
| | | unsigned long duty_cycle = 0; |
| | | char period_str[20] = {}; |
| | | char duty_cycle_str[20] = {}; |
| | | |
| | | if( !note || note->duty > 100 ) |
| | | { |
| | | printf("[INFO] %s argument error.\n", __FUNCTION__); |
| | | return -1; |
| | | } |
| | | |
| | | period = (unsigned long)((1.f / (double)note->freq) * 1e9);//ns单位 |
| | | duty_cycle = (unsigned long)(((double)note->duty / 100.f) * (double)period);//ns单位 |
| | | |
| | | snprintf(period_str, sizeof(period_str), "%lu", period); |
| | | snprintf(duty_cycle_str, sizeof(duty_cycle_str), "%lu", duty_cycle); |
| | | |
| | | //设置pwm频率和周期 |
| | | if (pwm_config("period", period_str)) |
| | | { |
| | | printf("pwm_config period failure.\n"); |
| | | return -1; |
| | | } |
| | | if (pwm_config("duty_cycle", duty_cycle_str)) |
| | | { |
| | | printf("pwm_config duty_cycle failure.\n"); |
| | | return -2; |
| | | } |
| | | |
| | | msleep(note->msec); |
| | | |
| | | /* 设置占空比为0 蜂鸣器无声 */ |
| | | if (pwm_config("duty_cycle", "0")) |
| | | { |
| | | printf("pwm_config duty_cycle failure.\n"); |
| | | return -3; |
| | | } |
| | | msleep(20); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /* ms级休眠函数 */ |
| | | static inline void msleep(unsigned long ms) |
| | | { |
| | | struct timespec cSleep; |
| | | unsigned long ulTmp; |
| | | |
| | | cSleep.tv_sec = ms / 1000; |
| | | if (cSleep.tv_sec == 0) |
| | | { |
| | | ulTmp = ms * 10000; |
| | | cSleep.tv_nsec = ulTmp * 100; |
| | | } |
| | | else |
| | | { |
| | | cSleep.tv_nsec = 0; |
| | | } |
| | | |
| | | nanosleep(&cSleep, 0); |
| | | } |
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2024 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: sht20_fops.c |
| | | * Description: This file is temperature and relative humidity sensor SHT20 code |
| | | * |
| | | * Version: 1.0.0(2024/05/08) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "2024/05/08 12:13:26" |
| | | * |
| | | * Pin connection: |
| | | * |
| | | * SHT20 Module IGKBoard |
| | | * VCC <-----> 3.3V |
| | | * SDA <-----> #Pin3(I2C1_SDA) |
| | | * SCL <-----> #Pin5(I2C1_SCL) |
| | | * GND <-----> GND |
| | | * |
| | | * /run/media/mmcblk1p1/config.txt: |
| | | * |
| | | * # Eanble I2C overlay |
| | | * dtoverlay_i2c=1 |
| | | ** |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <string.h> |
| | | #include <stdint.h> |
| | | #include <string.h> |
| | | #include <unistd.h> |
| | | #include <time.h> |
| | | #include <errno.h> |
| | | #include <fcntl.h> |
| | | #include <sys/ioctl.h> |
| | | #include <sys/types.h> |
| | | #include <sys/stat.h> |
| | | #include <linux/types.h> |
| | | #include <linux/i2c.h> |
| | | #include <linux/i2c-dev.h> |
| | | |
| | | int sht20_checksum(uint8_t *data, int len, int8_t checksum); |
| | | static inline void msleep(unsigned long ms); |
| | | static inline void dump_buf(const char *prompt, uint8_t *buf, int size); |
| | | |
| | | int main(int argc, char **argv) |
| | | { |
| | | int fd, rv; |
| | | float temp, rh; |
| | | char *i2c_dev = NULL; |
| | | uint8_t buf[4]; |
| | | |
| | | |
| | | if( argc != 2) |
| | | { |
| | | printf("This program used to do I2C test by SHT20 sensor.\n"); |
| | | printf("Usage: %s /dev/i2c-x\n", argv[0]); |
| | | return 0; |
| | | } |
| | | |
| | | i2c_dev = argv[1]; |
| | | |
| | | /*+--------------------------------+ |
| | | *| open /dev/i2c-x device | |
| | | *+--------------------------------+*/ |
| | | if( (fd=open(i2c_dev, O_RDWR)) < 0) |
| | | { |
| | | printf("i2c device open failed: %s\n", strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | /*+--------------------------------+ |
| | | *| set I2C mode and slave address | |
| | | *+--------------------------------+*/ |
| | | ioctl(fd, I2C_TENBIT, 0); /* Not 10-bit but 7-bit mode */ |
| | | ioctl(fd, I2C_SLAVE, 0x40); /* set SHT2x slava address 0x40 */ |
| | | |
| | | /*+--------------------------------+ |
| | | *| software reset SHT20 sensor | |
| | | *+--------------------------------+*/ |
| | | |
| | | buf[0] = 0xFE; |
| | | write(fd, buf, 1); |
| | | |
| | | msleep(50); |
| | | |
| | | |
| | | /*+--------------------------------+ |
| | | *| trigger temperature measure | |
| | | *+--------------------------------+*/ |
| | | |
| | | buf[0] = 0xF3; |
| | | write(fd, buf, 1); |
| | | |
| | | msleep(85); /* datasheet: typ=66, max=85 */ |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | read(fd, buf, 3); |
| | | dump_buf("Temperature sample data: ", buf, 3); |
| | | |
| | | if( !sht20_checksum(buf, 2, buf[2]) ) |
| | | { |
| | | printf("Temperature sample data CRC checksum failure.\n"); |
| | | goto cleanup; |
| | | } |
| | | |
| | | temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85; |
| | | |
| | | |
| | | /*+--------------------------------+ |
| | | *| trigger humidity measure | |
| | | *+--------------------------------+*/ |
| | | |
| | | buf[0] = 0xF5; |
| | | write(fd, buf, 1); |
| | | |
| | | msleep(29); /* datasheet: typ=22, max=29 */ |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | read(fd, buf, 3); |
| | | dump_buf("Relative humidity sample data: ", buf, 3); |
| | | |
| | | if( !sht20_checksum(buf, 2, buf[2]) ) |
| | | { |
| | | printf("Relative humidity sample data CRC checksum failure.\n"); |
| | | goto cleanup; |
| | | } |
| | | |
| | | rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6; |
| | | |
| | | /*+--------------------------------+ |
| | | *| print the measure result | |
| | | *+--------------------------------+*/ |
| | | |
| | | printf("Temperature=%lf 'C relative humidity=%lf%%\n", temp, rh); |
| | | |
| | | cleanup: |
| | | close(fd); |
| | | return 0; |
| | | } |
| | | |
| | | int sht20_checksum(uint8_t *data, int len, int8_t checksum) |
| | | { |
| | | int8_t crc = 0; |
| | | int8_t bit; |
| | | int8_t byteCtr; |
| | | |
| | | //calculates 8-Bit checksum with given polynomial: x^8 + x^5 + x^4 + 1 |
| | | for (byteCtr = 0; byteCtr < len; ++byteCtr) |
| | | { |
| | | crc ^= (data[byteCtr]); |
| | | for ( bit = 8; bit > 0; --bit) |
| | | { |
| | | /* x^8 + x^5 + x^4 + 1 = 0001 0011 0001 = 0x131 */ |
| | | if (crc & 0x80) crc = (crc << 1) ^ 0x131; |
| | | else crc = (crc << 1); |
| | | } |
| | | } |
| | | |
| | | if (crc != checksum) |
| | | return 0; |
| | | else |
| | | return 1; |
| | | } |
| | | |
| | | static inline void msleep(unsigned long ms) |
| | | { |
| | | struct timespec cSleep; |
| | | unsigned long ulTmp; |
| | | |
| | | cSleep.tv_sec = ms / 1000; |
| | | if (cSleep.tv_sec == 0) |
| | | { |
| | | ulTmp = ms * 10000; |
| | | cSleep.tv_nsec = ulTmp * 100; |
| | | } |
| | | else |
| | | { |
| | | cSleep.tv_nsec = 0; |
| | | } |
| | | |
| | | nanosleep(&cSleep, 0); |
| | | return ; |
| | | } |
| | | |
| | | static inline void dump_buf(const char *prompt, uint8_t *buf, int size) |
| | | { |
| | | int i; |
| | | |
| | | if( !buf ) |
| | | { |
| | | return ; |
| | | } |
| | | |
| | | if( prompt ) |
| | | { |
| | | printf("%-32s ", prompt); |
| | | } |
| | | |
| | | for(i=0; i<size; i++) |
| | | { |
| | | printf("%02x ", buf[i]); |
| | | } |
| | | printf("\n"); |
| | | |
| | | return ; |
| | | } |
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2024 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: sht20_ioctl.c |
| | | * Description: This file is temperature and relative humidity sensor SHT20 code |
| | | * |
| | | * Version: 1.0.0(2024/05/08) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "2024/05/08 12:13:26" |
| | | * |
| | | * Pin connection: |
| | | * |
| | | * SHT20 Module IGKBoard |
| | | * VCC <-----> 3.3V |
| | | * SDA <-----> #Pin3(I2C1_SDA) |
| | | * SCL <-----> #Pin5(I2C1_SCL) |
| | | * GND <-----> GND |
| | | * |
| | | * /run/media/mmcblk1p1/config.txt: |
| | | * |
| | | * # Eanble I2C overlay |
| | | * dtoverlay_i2c=1 |
| | | ** |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <string.h> |
| | | #include <stdint.h> |
| | | #include <string.h> |
| | | #include <unistd.h> |
| | | #include <time.h> |
| | | #include <errno.h> |
| | | #include <fcntl.h> |
| | | #include <sys/ioctl.h> |
| | | #include <sys/types.h> |
| | | #include <sys/stat.h> |
| | | #include <linux/types.h> |
| | | #include <linux/i2c.h> |
| | | #include <linux/i2c-dev.h> |
| | | |
| | | int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len); |
| | | int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size); |
| | | int sht20_checksum(uint8_t *data, int len, int8_t checksum); |
| | | static inline void msleep(unsigned long ms); |
| | | static inline void dump_buf(const char *prompt, uint8_t *buf, int size); |
| | | |
| | | int main(int argc, char **argv) |
| | | { |
| | | int fd, rv; |
| | | float temp, rh; |
| | | char *i2c_dev = NULL; |
| | | uint8_t buf[4]; |
| | | |
| | | |
| | | if( argc != 2) |
| | | { |
| | | printf("This program used to do I2C test by SHT20 sensor.\n"); |
| | | printf("Usage: %s /dev/i2c-x\n", argv[0]); |
| | | return 0; |
| | | } |
| | | |
| | | i2c_dev = argv[1]; |
| | | |
| | | /*+--------------------------------+ |
| | | *| open /dev/i2c-x device | |
| | | *+--------------------------------+*/ |
| | | if( (fd=open(i2c_dev, O_RDWR)) < 0) |
| | | { |
| | | printf("i2c device open failed: %s\n", strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | /*+--------------------------------+ |
| | | *| software reset SHT20 sensor | |
| | | *+--------------------------------+*/ |
| | | |
| | | buf[0] = 0xFE; |
| | | i2c_write(fd, 0x40, buf, 1); |
| | | |
| | | msleep(50); |
| | | |
| | | |
| | | /*+--------------------------------+ |
| | | *| trigger temperature measure | |
| | | *+--------------------------------+*/ |
| | | |
| | | buf[0] = 0xF3; |
| | | i2c_write(fd, 0x40, buf, 1); |
| | | |
| | | msleep(85); /* datasheet: typ=66, max=85 */ |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | i2c_read(fd, 0x40, buf, 3); |
| | | dump_buf("Temperature sample data: ", buf, 3); |
| | | |
| | | if( !sht20_checksum(buf, 2, buf[2]) ) |
| | | { |
| | | printf("Temperature sample data CRC checksum failure.\n"); |
| | | goto cleanup; |
| | | } |
| | | |
| | | temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85; |
| | | |
| | | |
| | | /*+--------------------------------+ |
| | | *| trigger humidity measure | |
| | | *+--------------------------------+*/ |
| | | |
| | | buf[0] = 0xF5; |
| | | i2c_write(fd, 0x40, buf, 1); |
| | | |
| | | msleep(29); /* datasheet: typ=22, max=29 */ |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | i2c_read(fd, 0x40, buf, 3); |
| | | dump_buf("Relative humidity sample data: ", buf, 3); |
| | | |
| | | if( !sht20_checksum(buf, 2, buf[2]) ) |
| | | { |
| | | printf("Relative humidity sample data CRC checksum failure.\n"); |
| | | goto cleanup; |
| | | } |
| | | |
| | | rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6; |
| | | |
| | | /*+--------------------------------+ |
| | | *| print the measure result | |
| | | *+--------------------------------+*/ |
| | | |
| | | printf("Temperature=%lf 'C relative humidity=%lf%%\n", temp, rh); |
| | | |
| | | cleanup: |
| | | close(fd); |
| | | return 0; |
| | | } |
| | | |
| | | int sht20_checksum(uint8_t *data, int len, int8_t checksum) |
| | | { |
| | | int8_t crc = 0; |
| | | int8_t bit; |
| | | int8_t byteCtr; |
| | | |
| | | //calculates 8-Bit checksum with given polynomial: x^8 + x^5 + x^4 + 1 |
| | | for (byteCtr = 0; byteCtr < len; ++byteCtr) |
| | | { |
| | | crc ^= (data[byteCtr]); |
| | | for ( bit = 8; bit > 0; --bit) |
| | | { |
| | | /* x^8 + x^5 + x^4 + 1 = 0001 0011 0001 = 0x131 */ |
| | | if (crc & 0x80) crc = (crc << 1) ^ 0x131; |
| | | else crc = (crc << 1); |
| | | } |
| | | } |
| | | |
| | | if (crc != checksum) |
| | | return 0; |
| | | else |
| | | return 1; |
| | | } |
| | | |
| | | int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len) |
| | | { |
| | | struct i2c_rdwr_ioctl_data i2cdata; |
| | | int rv = 0; |
| | | |
| | | if( !data || len<= 0) |
| | | { |
| | | printf("%s() invalid input arguments!\n", __func__); |
| | | return -1; |
| | | } |
| | | |
| | | i2cdata.nmsgs = 1; |
| | | i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs ); |
| | | if ( !i2cdata.msgs ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | return -2; |
| | | } |
| | | |
| | | i2cdata.msgs[0].addr = slave_addr; |
| | | i2cdata.msgs[0].flags = 0; //write |
| | | i2cdata.msgs[0].len = len; |
| | | i2cdata.msgs[0].buf = malloc(len); |
| | | if( !i2cdata.msgs[0].buf ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | rv = -3; |
| | | goto cleanup; |
| | | } |
| | | memcpy(i2cdata.msgs[0].buf, data, len); |
| | | |
| | | |
| | | if( ioctl(fd, I2C_RDWR, &i2cdata)<0 ) |
| | | { |
| | | printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | rv = -4; |
| | | goto cleanup; |
| | | } |
| | | |
| | | cleanup: |
| | | if( i2cdata.msgs[0].buf ) |
| | | free(i2cdata.msgs[0].buf); |
| | | |
| | | if( i2cdata.msgs ) |
| | | free(i2cdata.msgs); |
| | | |
| | | return rv; |
| | | } |
| | | |
| | | int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size) |
| | | { |
| | | struct i2c_rdwr_ioctl_data i2cdata; |
| | | int rv = 0; |
| | | |
| | | if( !buf || size<= 0) |
| | | { |
| | | printf("%s() invalid input arguments!\n", __func__); |
| | | return -1; |
| | | } |
| | | |
| | | i2cdata.nmsgs = 1; |
| | | i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs ); |
| | | if ( !i2cdata.msgs ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | return -2; |
| | | } |
| | | |
| | | i2cdata.msgs[0].addr = slave_addr; |
| | | i2cdata.msgs[0].flags = I2C_M_RD; //read |
| | | i2cdata.msgs[0].len = size; |
| | | i2cdata.msgs[0].buf = buf; |
| | | memset(buf, 0, size); |
| | | |
| | | if( ioctl(fd, I2C_RDWR, &i2cdata)<0 ) |
| | | { |
| | | printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | rv = -4; |
| | | } |
| | | |
| | | free( i2cdata.msgs ); |
| | | return rv; |
| | | } |
| | | |
| | | |
| | | static inline void msleep(unsigned long ms) |
| | | { |
| | | struct timespec cSleep; |
| | | unsigned long ulTmp; |
| | | |
| | | cSleep.tv_sec = ms / 1000; |
| | | if (cSleep.tv_sec == 0) |
| | | { |
| | | ulTmp = ms * 10000; |
| | | cSleep.tv_nsec = ulTmp * 100; |
| | | } |
| | | else |
| | | { |
| | | cSleep.tv_nsec = 0; |
| | | } |
| | | |
| | | nanosleep(&cSleep, 0); |
| | | return ; |
| | | } |
| | | |
| | | static inline void dump_buf(const char *prompt, uint8_t *buf, int size) |
| | | { |
| | | int i; |
| | | |
| | | if( !buf ) |
| | | { |
| | | return ; |
| | | } |
| | | |
| | | if( prompt ) |
| | | { |
| | | printf("%-32s ", prompt); |
| | | } |
| | | |
| | | for(i=0; i<size; i++) |
| | | { |
| | | printf("%02x ", buf[i]); |
| | | } |
| | | printf("\n"); |
| | | |
| | | return ; |
| | | } |
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2024 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: spi_test.c |
| | | * Description: This file is SPI loop test program |
| | | * |
| | | * Version: 1.0.0(05/23/2024) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "05/23/2024 07:51:06 PM" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <stdint.h> |
| | | #include <unistd.h> |
| | | #include <string.h> |
| | | #include <errno.h> |
| | | #include <getopt.h> |
| | | #include <libgen.h> |
| | | #include <fcntl.h> |
| | | #include <sys/types.h> |
| | | #include <sys/stat.h> |
| | | #include <sys/ioctl.h> |
| | | #include <linux/spi/spidev.h> |
| | | |
| | | typedef struct spi_ctx_s |
| | | { |
| | | int fd; |
| | | char dev[64]; |
| | | uint8_t bits; |
| | | uint16_t delay; |
| | | uint32_t mode; |
| | | uint32_t speed; |
| | | } spi_ctx_t; |
| | | |
| | | static int spi_init(spi_ctx_t *spi_ctx); |
| | | static int transfer(spi_ctx_t *spi_ctx, uint8_t const *tx, uint8_t const *rx, size_t len); |
| | | |
| | | static void program_usage(char *progname) |
| | | { |
| | | printf("Usage: %s [OPTION]...\n", progname); |
| | | printf(" %s is a program to test IGKBoard loop spi\n", progname); |
| | | |
| | | printf("\nMandatory arguments to long options are mandatory for short options too:\n"); |
| | | printf(" -d[device ] Specify SPI device, such as: /dev/spidev0.0\n"); |
| | | printf(" -s[speed ] max speed (Hz), such as: -s 500000\n"); |
| | | printf(" -p[print ] Send data (such as: -p 1234/xde/xad)\n"); |
| | | |
| | | printf("\n%s version 1.0\n", progname); |
| | | return; |
| | | } |
| | | |
| | | int main(int argc,char * argv[]) |
| | | { |
| | | spi_ctx_t spi_ctx; |
| | | uint32_t spi_speed = 500000; // Default SPI speed 500KHz |
| | | char *spi_dev = "/dev/spidev0.0"; // Default SPI device |
| | | char *input_tx = "Hello LingYun"; // Default transfer data |
| | | uint8_t rx_buffer[128]; |
| | | int opt; |
| | | char *progname=NULL; |
| | | |
| | | struct option long_options[] = { |
| | | {"device", required_argument, NULL, 'd'}, |
| | | {"speed", required_argument, NULL, 's'}, |
| | | {"print", required_argument, NULL, 'p'}, |
| | | {"help", no_argument, NULL, 'h'}, |
| | | {NULL, 0, NULL, 0} |
| | | }; |
| | | |
| | | progname = (char *)basename(argv[0]); |
| | | |
| | | while((opt = getopt_long(argc, argv, "d:s:p:h", long_options, NULL)) != -1) |
| | | { |
| | | switch (opt) |
| | | { |
| | | case 'd': |
| | | spi_dev = optarg; |
| | | break; |
| | | |
| | | case 's': |
| | | spi_speed = atoi(optarg); |
| | | break; |
| | | |
| | | case 'p': |
| | | input_tx = optarg; |
| | | break; |
| | | |
| | | case 'h': |
| | | program_usage(progname); |
| | | return 0; |
| | | |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | memset(&spi_ctx, 0, sizeof(spi_ctx)); |
| | | strncpy(spi_ctx.dev, spi_dev, sizeof(spi_ctx.dev)); |
| | | spi_ctx.bits = 8; |
| | | spi_ctx.delay = 100; |
| | | spi_ctx.mode = SPI_MODE_2; |
| | | spi_ctx.speed = spi_speed; |
| | | |
| | | if( spi_init(&spi_ctx) < 0 ) |
| | | { |
| | | printf("Initial SPI device '%s' failed.\n", spi_ctx.dev); |
| | | return -1; |
| | | } |
| | | printf("Initial SPI device '%s' okay.\n", spi_ctx.dev); |
| | | |
| | | if ( transfer(&spi_ctx, input_tx, rx_buffer, strlen(input_tx)) < 0 ) |
| | | { |
| | | printf("spi transfer error\n"); |
| | | return -2; |
| | | } |
| | | |
| | | printf("tx_buffer: | %s |\n", input_tx); |
| | | printf("rx_buffer: | %s |\n", rx_buffer); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int transfer(spi_ctx_t *spi_ctx, uint8_t const *tx, uint8_t const *rx, size_t len) |
| | | { |
| | | struct spi_ioc_transfer tr = { |
| | | .tx_buf = (unsigned long )tx, |
| | | .rx_buf = (unsigned long )rx, |
| | | .len = len, |
| | | .delay_usecs = spi_ctx->delay, |
| | | .speed_hz = spi_ctx->speed, |
| | | .bits_per_word = spi_ctx->bits, |
| | | }; |
| | | |
| | | if( ioctl(spi_ctx->fd, SPI_IOC_MESSAGE(1), &tr) < 0) |
| | | { |
| | | printf("ERROR: SPI transfer failure: %s\n ", strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int spi_init(spi_ctx_t *spi_ctx) |
| | | { |
| | | int ret; |
| | | spi_ctx->fd = open(spi_ctx->dev, O_RDWR); |
| | | if(spi_ctx->fd < 0) |
| | | { |
| | | printf("open %s error\n", spi_ctx->dev); |
| | | return -1; |
| | | } |
| | | |
| | | ret = ioctl(spi_ctx->fd, SPI_IOC_RD_MODE, &spi_ctx->mode); |
| | | if( ret < 0 ) |
| | | { |
| | | printf("ERROR: SPI set SPI_IOC_RD_MODE [0x%x] failure: %s\n ", spi_ctx->mode, strerror(errno)); |
| | | goto cleanup; |
| | | } |
| | | |
| | | ret = ioctl(spi_ctx->fd, SPI_IOC_WR_MODE, &spi_ctx->mode); |
| | | if( ret < 0 ) |
| | | { |
| | | printf("ERROR: SPI set SPI_IOC_WR_MODE [0x%x] failure: %s\n ", spi_ctx->mode, strerror(errno)); |
| | | goto cleanup; |
| | | } |
| | | |
| | | ret = ioctl(spi_ctx->fd, SPI_IOC_RD_BITS_PER_WORD, &spi_ctx->bits); |
| | | if( ret < 0 ) |
| | | { |
| | | printf("ERROR: SPI set SPI_IOC_RD_BITS_PER_WORD [%d] failure: %s\n ", spi_ctx->bits, strerror(errno)); |
| | | goto cleanup; |
| | | } |
| | | ret = ioctl(spi_ctx->fd, SPI_IOC_WR_BITS_PER_WORD, &spi_ctx->bits); |
| | | if( ret < 0 ) |
| | | { |
| | | printf("ERROR: SPI set SPI_IOC_WR_BITS_PER_WORD [%d] failure: %s\n ", spi_ctx->bits, strerror(errno)); |
| | | goto cleanup; |
| | | } |
| | | |
| | | ret = ioctl(spi_ctx->fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_ctx->speed); |
| | | if( ret == -1) |
| | | { |
| | | printf("ERROR: SPI set SPI_IOC_WR_MAX_SPEED_HZ [%d] failure: %s\n ", spi_ctx->speed, strerror(errno)); |
| | | goto cleanup; |
| | | } |
| | | ret = ioctl(spi_ctx->fd, SPI_IOC_RD_MAX_SPEED_HZ, &spi_ctx->speed); |
| | | if( ret == -1) |
| | | { |
| | | printf("ERROR: SPI set SPI_IOC_RD_MAX_SPEED_HZ [%d] failure: %s\n ", spi_ctx->speed, strerror(errno)); |
| | | goto cleanup; |
| | | } |
| | | |
| | | printf("spi mode: 0x%x\n", spi_ctx->mode); |
| | | printf("bits per word: %d\n", spi_ctx->bits); |
| | | printf("max speed: %d Hz (%d KHz)\n", spi_ctx->speed, spi_ctx->speed / 1000); |
| | | |
| | | return spi_ctx->fd; |
| | | |
| | | cleanup: |
| | | close(spi_ctx->fd); |
| | | return -1; |
| | | } |
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2024 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: ttyS_test.c |
| | | * Description: This file is comport loop back test program |
| | | * |
| | | * Version: 1.0.0(05/24/2024) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "05/24/2024 07:38:43 PM" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <string.h> |
| | | #include <unistd.h> |
| | | #include <fcntl.h> |
| | | #include <errno.h> |
| | | #include <termios.h> |
| | | #include <pthread.h> |
| | | |
| | | #define DEV_NAME "/dev/ttymxc1" |
| | | #define MSG "Hello, LingYun IoT System Studio!" |
| | | |
| | | int main(int argc, char **argv) |
| | | { |
| | | struct termios tty; |
| | | int serial_fd; |
| | | int bytes; |
| | | char read_buf[64]; |
| | | |
| | | /*+-------------------------+ |
| | | | Open Serial Port | |
| | | +-------------------------+*/ |
| | | serial_fd = open(DEV_NAME, O_RDWR ); |
| | | if (serial_fd == -1) |
| | | { |
| | | printf("Open '%s' failed: %s\n", DEV_NAME, strerror(errno)); |
| | | return 0; |
| | | } |
| | | |
| | | /*+-------------------------+ |
| | | | Configure Serial Port | |
| | | +-------------------------+*/ |
| | | |
| | | tcgetattr(serial_fd, &tty);// read current serial port settings |
| | | |
| | | tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) |
| | | tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) |
| | | tty.c_cflag &= ~CSIZE; // Clear all bits that set the data size |
| | | tty.c_cflag |= CS8; // 8 bits per byte (most common) |
| | | tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) |
| | | tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) |
| | | |
| | | tty.c_lflag &= ~ICANON; |
| | | tty.c_lflag &= ~ECHO; // Disable echo |
| | | tty.c_lflag &= ~ECHOE; // Disable erasure |
| | | tty.c_lflag &= ~ECHONL; // Disable new-line echo |
| | | tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP |
| | | tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl |
| | | tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes |
| | | |
| | | tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) |
| | | tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed |
| | | |
| | | tty.c_cc[VTIME] = 10; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received. |
| | | tty.c_cc[VMIN] = 0; |
| | | |
| | | /* Set in/out baud rate to be 115200 */ |
| | | cfsetispeed(&tty, B115200); |
| | | cfsetospeed(&tty, B115200); |
| | | |
| | | tcsetattr(serial_fd, TCSANOW, &tty); |
| | | |
| | | /*+-------------------------+ |
| | | | Write to Serial Port | |
| | | +-------------------------+*/ |
| | | write(serial_fd, MSG, strlen(MSG)); |
| | | printf("Send MSG : %s\n", MSG); |
| | | |
| | | /*+-------------------------+ |
| | | | Read from Serial Port | |
| | | +-------------------------+*/ |
| | | /* |
| | | * The behaviour of read() (e.g. does it block?, how long does it block for?) |
| | | * depends on the configuration settings above, specifically VMIN and VTIME |
| | | */ |
| | | memset(&read_buf, '\0', sizeof(read_buf)); |
| | | bytes = read(serial_fd, &read_buf, sizeof(read_buf)); |
| | | if (bytes< 0) |
| | | { |
| | | printf("Error reading: %s", strerror(errno)); |
| | | goto cleanup; |
| | | } |
| | | |
| | | printf("Received MSG: %s\n", read_buf); |
| | | |
| | | /*+-------------------------+ |
| | | | close Serial Port | |
| | | +-------------------------+*/ |
| | | |
| | | cleanup: |
| | | close(serial_fd); |
| | | return 0; // success |
| | | } |