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