From 1e563e2b731d928942f43e1341c8c50b0faf1c01 Mon Sep 17 00:00:00 2001 From: guowenxue <guowenxue@gmail.com> Date: Fri, 31 May 2024 11:39:31 +0800 Subject: [PATCH] APPS:IGKBoard-IMX6ULL: Add test-apps source code: --- drivers/test-apps/hello.c | 21 drivers/test-apps/sht20_fops.c | 208 +++++ drivers/test-apps/sht20_ioctl.c | 289 +++++++ drivers/test-apps/ttyS_test.c | 106 ++ drivers/test-apps/can_test.c | 191 ++++ drivers/test-apps/libgpiod/build.sh | 118 +++ drivers/test-apps/makefile | 50 + drivers/test-apps/spi_test.c | 205 +++++ drivers/test-apps/leds.c | 332 ++++++++ drivers/test-apps/ds18b20.c | 38 drivers/test-apps/pwm_play.c | 214 +++++ drivers/test-apps/pwm.c | 341 ++++++++ drivers/test-apps/libgpiod/makefile | 11 drivers/test-apps/keypad.c | 197 +++++ 14 files changed, 2,296 insertions(+), 25 deletions(-) diff --git a/drivers/test-apps/can_test.c b/drivers/test-apps/can_test.c new file mode 100644 index 0000000..7570d51 --- /dev/null +++ b/drivers/test-apps/can_test.c @@ -0,0 +1,191 @@ +/********************************************************************************* + * 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; +} + diff --git a/drivers/test-apps/ds18b20.c b/drivers/test-apps/ds18b20.c index c9da5f3..caf8933 100644 --- a/drivers/test-apps/ds18b20.c +++ b/drivers/test-apps/ds18b20.c @@ -23,7 +23,6 @@ * ********************************************************************************/ -/* 在C语言编程时,一般系统的头文件用<xxx.h>,我们自己写的头文件则用"zzz.h" */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -33,17 +32,12 @@ #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"); @@ -64,15 +58,15 @@ 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语言编程时,进入函数的第一件事应该进行函数参数的合法性检测,检查参数非法输入。 @@ -110,21 +104,17 @@ /* 文件夹打开用完后,要记得第一时间关闭 */ 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)); @@ -136,13 +126,11 @@ { 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; diff --git a/drivers/test-apps/hello.c b/drivers/test-apps/hello.c new file mode 100644 index 0000000..279e45c --- /dev/null +++ b/drivers/test-apps/hello.c @@ -0,0 +1,21 @@ +/********************************************************************************* + * 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; +} diff --git a/drivers/test-apps/keypad.c b/drivers/test-apps/keypad.c new file mode 100644 index 0000000..3426c1b --- /dev/null +++ b/drivers/test-apps/keypad.c @@ -0,0 +1,197 @@ +/********************************************************************************* + * 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); + } + } +} diff --git a/drivers/test-apps/leds.c b/drivers/test-apps/leds.c new file mode 100644 index 0000000..1918a0b --- /dev/null +++ b/drivers/test-apps/leds.c @@ -0,0 +1,332 @@ +/********************************************************************************* + * 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 ; +} diff --git a/drivers/test-apps/libgpiod/build.sh b/drivers/test-apps/libgpiod/build.sh new file mode 100755 index 0000000..6009e21 --- /dev/null +++ b/drivers/test-apps/libgpiod/build.sh @@ -0,0 +1,118 @@ +#!/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 diff --git a/drivers/test-apps/libgpiod/makefile b/drivers/test-apps/libgpiod/makefile new file mode 100644 index 0000000..4cc8cac --- /dev/null +++ b/drivers/test-apps/libgpiod/makefile @@ -0,0 +1,11 @@ + +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 diff --git a/drivers/test-apps/makefile b/drivers/test-apps/makefile new file mode 100644 index 0000000..94dc8b9 --- /dev/null +++ b/drivers/test-apps/makefile @@ -0,0 +1,50 @@ + +# 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 diff --git a/drivers/test-apps/pwm.c b/drivers/test-apps/pwm.c new file mode 100644 index 0000000..c855da6 --- /dev/null +++ b/drivers/test-apps/pwm.c @@ -0,0 +1,341 @@ +/********************************************************************************* + * 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 ; +} diff --git a/drivers/test-apps/pwm_play.c b/drivers/test-apps/pwm_play.c new file mode 100644 index 0000000..63cc028 --- /dev/null +++ b/drivers/test-apps/pwm_play.c @@ -0,0 +1,214 @@ +/********************************************************************************* + * 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); +} diff --git a/drivers/test-apps/sht20_fops.c b/drivers/test-apps/sht20_fops.c new file mode 100644 index 0000000..af3c03e --- /dev/null +++ b/drivers/test-apps/sht20_fops.c @@ -0,0 +1,208 @@ +/********************************************************************************* + * 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 ; +} diff --git a/drivers/test-apps/sht20_ioctl.c b/drivers/test-apps/sht20_ioctl.c new file mode 100644 index 0000000..9f95d05 --- /dev/null +++ b/drivers/test-apps/sht20_ioctl.c @@ -0,0 +1,289 @@ +/********************************************************************************* + * 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 ; +} diff --git a/drivers/test-apps/spi_test.c b/drivers/test-apps/spi_test.c new file mode 100644 index 0000000..8d80e8a --- /dev/null +++ b/drivers/test-apps/spi_test.c @@ -0,0 +1,205 @@ +/********************************************************************************* + * 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; +} diff --git a/drivers/test-apps/ttyS_test.c b/drivers/test-apps/ttyS_test.c new file mode 100644 index 0000000..80ee223 --- /dev/null +++ b/drivers/test-apps/ttyS_test.c @@ -0,0 +1,106 @@ +/********************************************************************************* + * 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 +} -- Gitblit v1.9.1