commit 356652e1fe0654b13f2bcb4ad7b65839d8d5245d
Author: Miaow <3703781@qq.com>
Date: Sun Jan 16 13:44:50 2022 +0800
original version
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3480399
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,67 @@
+# ===Windows===
+# Windows thumbnail cache files
+**/Thumbs.db
+**/Thumbs.db:encryptable
+**/ehthumbs.db
+**/ehthumbs_vista.db
+
+# Dump file
+**/*.stackdump
+
+# Folder config file
+**/[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+**/$RECYCLE.BIN/
+
+# Windows Installer files
+**/*.cab
+**/*.msi
+**/*.msix
+**/*.msm
+**/*.msp
+
+# Windows shortcuts
+**/*.lnk
+
+# ===Linux===
+
+**/*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+**/.fuse_hidden*
+
+# KDE directory preferences
+**/.directory
+
+# Linux trash folder which might appear on any partition or disk
+**/.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+**/.nfs*
+
+# ===MacOS===
+# General
+**/.DS_Store
+**/.AppleDouble
+**/.LSOverride
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
\ No newline at end of file
diff --git a/LISCENSE b/LISCENSE
new file mode 100644
index 0000000..d69270b
--- /dev/null
+++ b/LISCENSE
@@ -0,0 +1 @@
+闭源,私有,保密
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a9f105c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+# 下位机
+
+下位机根据传送带脉冲等触发相机,接收上位机给的数据,按其要求控制阀板,用在各种分选机上。
+
+## 目录结构
+
+- binary为编译好的可执行文件
+- script为配置系统、安装环境、安装可执行文件、卸载可执行文件等的脚本
+- protocol为上位机和下位机通信的协议
+- hardware下位机主板、接口板、底板等的硬件设计
+- source为可执行文件的源程序
+
+## 版本
+
+由于经常有不同类型的新要求出现,比如分选糖果、分选烟梗、同为糖果也具有不同的参数,因此不同的下位机型号(注意不是更新,比如同一台机器需要设置新的参数)应建立不同的分支,**主分支无实际意义**
+
+分支命名规则(不使用中文,小写无空格)
+
+```shell
+b分支编号-p生产环境项目名-t分选对象[-其他特点1[-其他特点2...]]
+```
+
+中括号在这里表示可省略的项,中括号本身不应出现在实际命名中,其他特点应字母打头,可有多个,"-"相连
+
+使用Git的tag功能定义版本(注意连着tag一起push),Github仓库的release功能同步发布最新版本
+
+版本号遵循定义如下(不使用中文,小写无空格)
+
+```shell
+b分支编号-n编译号-h硬件版本-p协议版本-s脚本版本-r代码版本
+```
+
+分支编号和分支命名中编号一致
+
+## 作者
+
+作者觉得还是不说明作者是谁比较好,免得毕业后有提着示波器的师弟师妹来问问题
\ No newline at end of file
diff --git a/binary/target b/binary/target
new file mode 100644
index 0000000..29dd2f8
Binary files /dev/null and b/binary/target differ
diff --git a/protocol/下位机和上位机通信协议.md b/protocol/下位机和上位机通信协议.md
new file mode 100644
index 0000000..738a868
--- /dev/null
+++ b/protocol/下位机和上位机通信协议.md
@@ -0,0 +1,34 @@
+| 起始 | 长度高 | 长度低 | 类型高 | 类型低 | 数据字节1 | ... | 数据字节n | 校验低 | 校验高 | 结束 |
+| ---- | ------ | ------ | ------ | ------ | --------- | ---- | --------- | ------ | ------ | ---- |
+| 0xAA | 0x00 | 0x03 | 's' | 't' | | | | 0xFF | 0xFF | 0xBB |
+
+长度=数据字节数+2,组成一个无符号16位数,校验字节随意给值即可
+
+类型
+
+- 命令
+
+ - 开始命令 st,长度3,数据0xFF
+ - 停止命令sp,长度3,数据0xFF
+ - 测试命令te,长度3,数据0xFF
+ - poweron命令po,长度3,数据0xFF
+ - 设置相机触发周期对应的脉冲数sc,长度4,数据位十进制字符串'5''0'表示值50,'5'在前,'0'在后
+ - 设置阀板动作对应的脉冲数sv,长度4,数据位十进制字符串'5''0'表示值50,'5'在前,'0'在后
+ - 设置相机触发到阀板动作的延迟脉冲数sd,长度4,数据位十进制字符串'5''0'表示值100,'5'在前,'0'在后
+ - 数据命令da, 长度为3002,数据要有3000个
+
+- 数据
+
+ 数据就是阀数据,其实这是一个命令,也就是数据命令'da',分到数据这一节写是因为它的参数格式和其他命令不同,下表为字节排序,接收时从左往右,也就是数据字节1先接收到,然后是数据字节2,最后是数据字节6。阀1代表面向阀板,最靠近右边的阀,所以最左边的为阀48
+
+| 数据字节1 | 数据字节2 | 数据字节3 | 数据字节4 | 数据字节5 | 数据字节6 |
+| --------- | --------- | --------- | --------- | --------- | --------- |
+| 阀48-41 | 阀40-33 | 阀32-25 | 阀24-17 | 阀16-9 | 阀8-1 |
+
+ 对于各个字节,其中的位是这么对应的,以数据字节2为例
+
+| 位7 | 位6 | 位5 | 位4 | 位3 | 位2 | 位1 | 位0 |
+| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
+| 阀8 | 阀7 | 阀6 | 阀5 | 阀4 | 阀3 | 阀2 | 阀1 |
+
+ 需要注意的是,这里的数据不是ascii编码的,是直接的数据字节。上面描述字节排序的表为hostcomputer图像中的一行对应的阀动作,一共500行,发送完一行后接着发送下一行,直到最后一行。因此一共有3000个字节的数据。
\ No newline at end of file
diff --git a/source/.gitignore b/source/.gitignore
new file mode 100644
index 0000000..90c5bbe
--- /dev/null
+++ b/source/.gitignore
@@ -0,0 +1,66 @@
+# Prerequisites
+*.d
+
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/c_cpp_properties.json
+!.vscode/*.code-snippets
+build/
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
\ No newline at end of file
diff --git a/source/.vscode/c_cpp_properties.json b/source/.vscode/c_cpp_properties.json
new file mode 100644
index 0000000..c775f0d
--- /dev/null
+++ b/source/.vscode/c_cpp_properties.json
@@ -0,0 +1,24 @@
+{
+ "configurations": [
+ {
+ "name": "ARM",
+ "includePath": [
+ "${workspaceFolder}/**",
+ "/home/miaow/software/arm-2011.03/lib/gcc/arm-none-linux-gnueabi/4.5.2/include/**"
+ ],
+ "defines": [],
+ "compilerPath": "/home/miaow/software/arm-2011.03/bin/arm-none-linux-gnueabi-gcc",
+ "cStandard": "gnu99",
+ "cppStandard": "gnu++17",
+ "intelliSenseMode": "linux-gcc-arm",
+ "configurationProvider": "ms-vscode.makefile-tools",
+ "browse": {
+ "path": [
+ "${workspaceFolder}",
+ "/home/miaow/software/arm-2011.03/lib/gcc/arm-none-linux-gnueabi/4.5.2/include"
+ ]
+ }
+ }
+ ],
+ "version": 4
+}
\ No newline at end of file
diff --git a/source/.vscode/settings.json b/source/.vscode/settings.json
new file mode 100644
index 0000000..f16e402
--- /dev/null
+++ b/source/.vscode/settings.json
@@ -0,0 +1,76 @@
+{
+ "files.associations": {
+ "stdlib.h": "c",
+ "assert.h": "c",
+ "valve_init.h": "c",
+ "gpio.h": "c",
+ "type_traits": "c",
+ "gpio_common.h": "c",
+ "unistd.h": "c",
+ "stdint.h": "c",
+ "stat.h": "c",
+ "types.h": "c",
+ "pthread.h": "c",
+ "pthreadtypes.h": "c",
+ "valve.h": "c",
+ "queue.h": "c",
+ "stdio.h": "c",
+ "encoder.h": "c",
+ "semaphore.h": "c"
+ },
+ "makefile.launchConfigurations": [
+ {
+ "cwd": "/home/miaow/zlg/epc9600",
+ "binaryPath": "/home/miaow/zlg/epc9600",
+ "binaryArgs": []
+ }
+ ],
+
+
+ // Doxygen documentation generator set
+ // 文件注释:版权信息模板
+ "doxdocgen.file.copyrightTag": [
+ "@copyright Copyright (c) {year} miaow"
+ ],
+ // 文件注释:自定义模块,这里我添加一个修改日志
+ "doxdocgen.file.customTag": [
+ "@par Changelog:",
+ "
",
+ "| Date | Version | Author | Description",
+ " |
|---|
| {date} | 1.0 | miaow | 内容",
+ " |
",
+ ],
+ // 文件注释的组成及其排序
+ "doxdocgen.file.fileOrder": [
+ "file", // @file
+ "brief", // @brief 简介
+ "author", // 作者
+ "version", // 版本
+ "date", // 日期
+ "empty", // 空行
+ "copyright",// 版权
+ "empty",
+ "custom" // 自定义
+ ],
+ // 下面时设置上面标签tag的具体信息
+ "doxdocgen.file.fileTemplate": "@file {name}",
+ "doxdocgen.file.versionTag": "@version 1.0",
+ "doxdocgen.generic.authorEmail": "3703781@qq.com",
+ "doxdocgen.generic.authorName": "miaow",
+ "doxdocgen.generic.authorTag": "@author {author} ({email})",
+ // 日期格式与模板
+ "doxdocgen.generic.dateFormat": "YYYY/MM/DD",
+ "doxdocgen.generic.dateTemplate": "@date {date}",
+
+ // 根据自动生成的注释模板(目前主要体现在函数注释上)
+ "doxdocgen.generic.order": [
+ "brief",
+ "tparam",
+ "param",
+ "return"
+ ],
+ "doxdocgen.generic.paramTemplate": "@param {param}",
+ "doxdocgen.generic.returnTemplate": "@return {type} ",
+ "doxdocgen.generic.splitCasingSmartText": true
+}
+
diff --git a/source/Makefile b/source/Makefile
new file mode 100644
index 0000000..31b1c4e
--- /dev/null
+++ b/source/Makefile
@@ -0,0 +1,86 @@
+#makefile for file_ioctl
+CROSS_COMPILE ?= /home/miaow/software/arm-2011.03/bin/arm-none-linux-gnueabi-
+TARGET := target
+BUILD_DIR := build
+
+ifeq ("$(origin V)", "command line")
+ KBUILD_VERBOSE = $(V)
+endif
+ifndef KBUILD_VERBOSE
+ KBUILD_VERBOSE = 0
+endif
+
+ifeq ($(KBUILD_VERBOSE),1)
+ quiet =
+ Q =
+else
+ quiet=quiet_
+ Q = @
+endif
+
+ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
+ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
+ quiet=silent_
+endif
+else # make-3.8x
+ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
+ quiet=silent_
+endif
+endif
+
+
+SRC := $(wildcard *.c)
+ASM_SRC := $(wildcard *.s)
+OBJ := $(addprefix $(BUILD_DIR)/, $(notdir $(SRC:.c=.o)))
+ASM_OBJ := $(addprefix $(BUILD_DIR)/, $(notdir $(ASM_SRC:.s=.o)))
+DIS := $(addprefix $(BUILD_DIR)/, $(notdir $(SRC:.c=.dis)))
+ASM_DIS := $(addprefix $(BUILD_DIR)/, $(notdir $(ASM_SRC:.s=.dis)))
+
+_TARGET := $(BUILD_DIR)/$(TARGET)
+TARGET_DIS := $(BUILD_DIR)/$(TARGET).dis
+
+LD = $(CROSS_COMPILE)ld
+CC = $(CROSS_COMPILE)gcc
+CPP = $(CC) -E
+AR = $(CROSS_COMPILE)ar
+LDR = $(CROSS_COMPILE)ldr
+STRIP = $(CROSS_COMPILE)strip
+OBJCOPY = $(CROSS_COMPILE)objcopy
+OBJDUMP = $(CROSS_COMPILE)objdump
+CFLAGS = -g -std=gnu99 -Wall -I.
+LDFLAGS = -lpthread -lc -lm -lrt -marmelf_linux_eabi
+
+.SECONDARY:
+
+.PHONY:all
+all: $(_TARGET) $(DIS) $(ASM_DIS) $(TARGET_DIS)
+
+$(BUILD_DIR)/%.i:%.c %.h Makefile | $(BUILD_DIR)
+ $(Q)$(CC) -E $(CFLAGS) $< -o $@
+$(BUILD_DIR)/%.s:$(BUILD_DIR)/%.i Makefile | $(BUILD_DIR)
+ $(Q)$(CC) -S $(CFLAGS) $< -o $@
+$(BUILD_DIR)/%.o:$(BUILD_DIR)/%.s Makefile | $(BUILD_DIR)
+ $(Q)$(CC) -c $(CFLAGS) $< -o $@
+$(BUILD_DIR)/%.o:%.s Makefile | $(BUILD_DIR)
+ $(Q)$(CC) -c $(CFLAGS) $< -o $@
+$(BUILD_DIR)/%.dis:$(BUILD_DIR)/%.o Makefile | $(BUILD_DIR)
+ $(Q)$(OBJDUMP) -s -d $< > $@
+$(TARGET_DIS):$(_TARGET) Makefile | $(BUILD_DIR)
+ $(Q)$(OBJDUMP) -s -d $< > $@
+$(_TARGET):$(OBJ) $(ASM_OBJ) Makefile | $(BUILD_DIR)
+ $(Q)$(CC) $(OBJ) $(ASM_OBJ) $(LDFLAGS) -o $@
+
+
+.PHONY:clean
+clean:
+ $(Q)$(RM) $(BUILD_DIR)/* -f
+
+.PHONY:install
+install:$(TARGET)
+ $(Q)chmod 777 $(TARGET)
+
+.PHONY:$(BUILD_DIR)
+$(BUILD_DIR):
+ $(Q)if [ ! -d $(BUILD_DIR) ]; then mkdir -p $@; fi
+
+
\ No newline at end of file
diff --git a/source/camera_trigger.c b/source/camera_trigger.c
new file mode 100644
index 0000000..bc91aa4
--- /dev/null
+++ b/source/camera_trigger.c
@@ -0,0 +1,141 @@
+/**
+ * @file camera_trigger.c
+ * @brief Control the camera to grab frames
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2022/01/09
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/09 | 1.0 | miaow | Write this file
+ * |
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Write to the file desc (global variable `gpo_value_fd` in gpio_common.c) to operate a gpio.
+// So gpo_value_fd should be initialized in valve_init with great care.
+// Also, gpo_value_fd/gpi_value_fd is used in other .c files (read pluse of encoder, etc).
+#define __GPO_SET_BIT(pin_t) __GPO_SET(pin_t, GPIO_VALUE_HIGH)
+#define __GPO_CLR_BIT(pin_t) __GPO_SET(pin_t, GPIO_VALUE_LOW)
+#define __GPO_SET(pin_t, value_t) write(gpo_value_fd[GPIO_PINDEF_TO_INDEX(pin_t)], gpio_pin_value_str[GPIO_VALUEDEF_TO_INDEX(value_t)], gpio_pin_value_str_len[GPIO_VALUEDEF_TO_INDEX(value_t)])
+
+/**
+ * @brief Variables definition used in this module
+ */
+typedef struct
+{
+ sem_t need_send; // Value >= 0 will cause a the camera grabbing one frame
+ sem_t is_sending; // value >= 0 means the last trigger signal is sent
+ pthread_mutex_t loop_thread_mutex;
+ int need_exit; // loop_thread joins to parent-thread at need_exit==1
+ pthread_t loop_thread; // The sending thread
+} cameratrigger_global_t;
+
+static cameratrigger_global_t _global_structure;
+
+static void *loop_thread_func(void *param);
+
+/**
+ * @brief Initialize camera trigger gpo and start loop_thread which keeps listening the trig signal
+ * @return 0 - success, -1 - error
+ */
+int cameratrigger_init()
+{
+ int trig_line_index = GPIO_PINDEF_TO_INDEX(TRIG_LINE);
+ // export the trigger line
+ int fd_export = open(GPIO_EXPORT_PATH, O_WRONLY);
+ ON_ERROR_RET(fd_export, GPIO_EXPORT_PATH, "export in cameratrigger_init()", -1);
+
+ if (!is_file_exist(gpio_value_file_gpo_list[trig_line_index]))
+ {
+ int ret = write(fd_export, gpo_pin_str[trig_line_index], gpo_pin_str_len[trig_line_index]);
+ ON_ERROR_RET(ret, gpo_pin_str[trig_line_index], "open value file in cameratrigger_init()", -1);
+ }
+ close(fd_export);
+
+ gpo_value_fd[trig_line_index] = open(gpio_value_file_gpo_list[trig_line_index], O_RDWR);
+ ON_ERROR_RET(gpo_value_fd[trig_line_index], gpio_value_file_gpo_list[trig_line_index], "open value file in cameratrigger_init()", -1);
+ __GPO_SET_BIT(TRIG_LINE);
+
+ sem_init(&_global_structure.need_send, 0, 0);
+ sem_init(&_global_structure.is_sending, 0, 1);
+ pthread_mutex_init(&_global_structure.loop_thread_mutex, NULL);
+
+ int ret = pthread_create(&_global_structure.loop_thread, NULL, loop_thread_func, NULL);
+ ON_ERROR_RET(ret, "thread create error in cameratrigger_init()", "", -1);
+
+ return 0;
+}
+
+/**
+ * @brief This function runs in child thread and triggles the camera to grab one frame
+ */
+void *loop_thread_func(void *param)
+{
+ printf("loop_thread in %s start\r\n", __FILE__);
+ int need_exit = 0;
+ struct timespec ts;
+ int ret = 0;
+ while (!need_exit)
+ {
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 1;
+ ret = sem_timedwait(&_global_structure.need_send, &ts);
+ if (ret == 0)
+ {
+ __GPO_CLR_BIT(TRIG_LINE);
+ usleep(200);
+ __GPO_SET_BIT(TRIG_LINE);
+ sem_post(&_global_structure.is_sending);
+ }
+ pthread_mutex_lock(&_global_structure.loop_thread_mutex);
+ need_exit = _global_structure.need_exit;
+ pthread_mutex_unlock(&_global_structure.loop_thread_mutex);
+ }
+ printf("loop_thread in %s exit\r\n", __FILE__);
+ return NULL;
+}
+
+/**
+ * @brief Trigger a frame grabbing of the camera
+ * @note This function will wait until last grabbing accomplished
+ * @return 0 - success
+ */
+int cameratrigger_trig()
+{
+ sem_wait(&_global_structure.is_sending);
+ sem_post(&_global_structure.need_send);
+ return 0;
+}
+
+/**
+ * @brief Deinitialize and release all resources of this module
+ * @note This function DOES BLOCKS 1s at most and DOES NOT UNEXPORT gpo
+ * @return 0 - success, -1 - error
+ */
+int cameratrigger_deinit()
+{
+ sem_wait(&_global_structure.is_sending);
+ pthread_mutex_lock(&_global_structure.loop_thread_mutex);
+ _global_structure.need_exit = 1;
+ pthread_mutex_unlock(&_global_structure.loop_thread_mutex);
+ pthread_join(_global_structure.loop_thread, NULL);
+ pthread_mutex_destroy(&_global_structure.loop_thread_mutex);
+ sem_destroy(&_global_structure.is_sending);
+ sem_destroy(&_global_structure.need_send);
+ _global_structure.need_exit = 0;
+
+ int ret = close(gpo_value_fd[GPIO_PINDEF_TO_INDEX(TRIG_LINE)]);
+ ON_ERROR_RET(ret, "close value file in cameratrigger_deinit()", "", -1);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/source/camera_trigger.h b/source/camera_trigger.h
new file mode 100644
index 0000000..5121d5c
--- /dev/null
+++ b/source/camera_trigger.h
@@ -0,0 +1,29 @@
+/**
+ * @file camera_trigger.c
+ * @brief Control the camera to grab frames
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2022/01/09
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/09 | 1.0 | miaow | Write this file
+ * |
+ */
+#ifndef __CAMERA_TRIGGER_H
+#define __CAMERA_TRIGGER_H
+#include
+
+typedef enum
+{
+ TRIG_LINE=GPIO_PINDEF_TO_INDEX(GPO6)
+}cameratrigger_pin_enum_t;
+
+int cameratrigger_init(void);
+int cameratrigger_trig(void);
+int cameratrigger_deinit(void);
+
+#endif
\ No newline at end of file
diff --git a/source/delay.s b/source/delay.s
new file mode 100644
index 0000000..d0258c0
--- /dev/null
+++ b/source/delay.s
@@ -0,0 +1,18 @@
+.global delay_us
+.func delay_us
+delay_us:
+ cmp r0, #0
+ moveq pc, lr
+ stmfd sp!, {r1, r2, fp, lr}
+ mov r1, r0
+ big_loop:
+ ldr r2, =266
+ loop:
+ sub r2, r2, #1
+ cmp r2, #0
+ bne loop
+ sub r1, r1, #1
+ cmp r1, #0
+ bne big_loop
+ ldmfd sp!, {r1, r2, fp, pc}
+.endfunc
diff --git a/source/encoder.c b/source/encoder.c
new file mode 100644
index 0000000..dd3c2bd
--- /dev/null
+++ b/source/encoder.c
@@ -0,0 +1,130 @@
+/**
+ * @file encoder.c
+ * @brief Manage the encoder and realize a callback function
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2022/01/09
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/09 | 1.0 | Miaow | Write this module
+ * |
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#define __GPI_GET(pin_t) read(gpi_value_fd[GPIO_PINDEF_TO_INDEX(pin_t)], _global_structure.buf, sizeof(_global_structure.buf))
+
+/**
+ * @brief Variables definition used in this module
+ */
+typedef struct
+{
+ int need_exit; // loop_thread joins to parent-thread at need_exit==1
+ pthread_t loop_thread; // The main deamon thread
+ encoder_callback callback_func; // Restore the pointer to callback function
+ pthread_mutex_t loop_thread_mutex; // Used in the main deamon thread and deinit function, surrounding the need_exit variable
+ char buf[1]; // Buffer for reading the file descripter
+} encoder_global_t;
+
+
+static encoder_global_t _global_structure; //! the global variables used in this file (module)
+
+static void *loop_thread_func(void *param);
+
+/**
+ * @brief Initialize the encoder related gpio and thread
+ * @param func The callback function, which is called at rising edge of the encoder
+ * @return 0-success, -1 - failed
+ */
+int encoder_init(encoder_callback func)
+{
+
+ int phase_b_index = GPIO_PINDEF_TO_INDEX(ENCODER_PHASEB);
+ // export
+ int fd_export = open(GPIO_EXPORT_PATH, O_WRONLY);
+ ON_ERROR_RET(fd_export, GPIO_EXPORT_PATH, "export in encoder_init()", -1);
+
+ if (!is_file_exist(gpio_value_file_gpi_list[phase_b_index])) // do not export if value file exist
+ {
+ int ret = write(fd_export, gpi_pin_str[phase_b_index], gpi_pin_str_len[phase_b_index]);
+ ON_ERROR_RET(ret, gpi_pin_str[phase_b_index], "open value file in encoder_init()", -1);
+ }
+ close(fd_export);
+
+ // open edge file
+ int edge_fd = open(gpio_edge_file_gpi_list[phase_b_index], O_RDWR);
+ ON_ERROR_RET(edge_fd, gpio_edge_file_gpi_list[phase_b_index], "open edge file in encoder_init()", -1);
+ write(edge_fd, "rising", 7);
+ close(edge_fd);
+
+ // open value file
+ gpi_value_fd[phase_b_index] = open(gpio_value_file_gpi_list[phase_b_index], O_RDWR);
+ ON_ERROR_RET(gpi_value_fd[phase_b_index], gpio_value_file_gpi_list[phase_b_index], "open value file in encoder_init()", -1);
+
+ _global_structure.callback_func = func;
+
+ // start loop thread
+ pthread_create(&_global_structure.loop_thread, NULL, loop_thread_func, NULL);
+
+ return 0;
+}
+
+/**
+ * @brief Deinitialize the encoder module, stop the thread and release resources
+ * @return 0-success, -1 - failed
+ */
+int encoder_deinit()
+{
+ // stop loop_thread
+ pthread_mutex_lock(&_global_structure.loop_thread_mutex);
+ _global_structure.need_exit = 1;
+ pthread_mutex_unlock(&_global_structure.loop_thread_mutex);
+ // wait loop_thread to stop
+ pthread_join(_global_structure.loop_thread, NULL);
+ pthread_mutex_destroy(&_global_structure.loop_thread_mutex);
+ _global_structure.need_exit = 0;
+ // close value file
+ int ret = close(gpi_value_fd[GPIO_PINDEF_TO_INDEX(ENCODER_PHASEB)]);
+ ON_ERROR_RET(ret, "close value file in encoder_init()", "", -1);
+ gpi_value_fd[GPIO_PINDEF_TO_INDEX(ENCODER_PHASEB)] = 0;
+ return 0;
+}
+
+/**
+ * @brief Call the callback function set when initialization at rising edge of the encoder pulse
+ * @param param Not used
+ * @return 0
+ */
+static void *loop_thread_func(void *param)
+{
+ // 调用一次encoder_callback func
+ printf("loop thread in %s start\r\n", __FILE__);
+ struct pollfd fds[1];
+ fds[0].fd = gpi_value_fd[GPIO_PINDEF_TO_INDEX(ENCODER_PHASEB)];
+ fds[0].events = POLLPRI;
+ int need_exit = 0;
+
+ while (!need_exit)
+ {
+ if (poll(fds, 1, 1000) && (fds[0].revents & POLLPRI))
+ {
+ __GPI_GET(ENCODER_PHASEB);
+ _global_structure.callback_func();
+ }
+ pthread_mutex_lock(&_global_structure.loop_thread_mutex);
+ need_exit = _global_structure.need_exit;
+ pthread_mutex_unlock(&_global_structure.loop_thread_mutex);
+ }
+ printf("loop thread in %s exit\r\n", __FILE__);
+ return (void *)NULL;
+}
+
diff --git a/source/encoder.h b/source/encoder.h
new file mode 100644
index 0000000..d0ccc37
--- /dev/null
+++ b/source/encoder.h
@@ -0,0 +1,35 @@
+/**
+ * @file encoder.h
+ * @brief Manage the encoder and realize a callback function
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2022/01/09
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/09 | 1.0 | Miaow | Write this module
+ * |
+ */
+#ifndef __ENCODER_H
+#define __ENCODER_H
+#include
+
+/**
+ * @brief Pin definition
+ * @note Actually, only ENCODER_PHASEB is used
+ */
+typedef enum
+{
+ ENCODER_PHASEA=GPIO_PINDEF_TO_INDEX(GPI0),
+ ENCODER_PHASEB=GPIO_PINDEF_TO_INDEX(GPI2)
+}encoder_pin_enum_t;
+
+typedef void (*encoder_callback)(void); // Callback funtion prototype.
+
+int encoder_init(encoder_callback func);
+int encoder_deinit(void);
+
+#endif
diff --git a/source/gpio_common.c b/source/gpio_common.c
new file mode 100644
index 0000000..0c4a6a1
--- /dev/null
+++ b/source/gpio_common.c
@@ -0,0 +1,73 @@
+/**
+ * @file gpio_common.c
+ * @brief Operate the GPIO port of Zhou Ligong linux industrial control board
+ * @details is_file_exist(const char *file_path) determine whether the specified file exists
+ * print_array(int *array, int count) used to print out the value of the queue buffer, easy to debug and use
+ * @mainpage github.com/NanjingForestryUniversity
+ * @author miaow
+ * @email 3703781@qq.com
+ * @version v0.9.0
+ * @date 2021/12/25 merry christmas
+ */
+#include
+
+char perror_buffer[1024] = {0};
+
+char *gpio_value_file_gpo_list[8] = {GPIO_GET_VALUE_FILE(52), GPIO_GET_VALUE_FILE(53),
+ GPIO_GET_VALUE_FILE(54), GPIO_GET_VALUE_FILE(55),
+ GPIO_GET_VALUE_FILE(56), GPIO_GET_VALUE_FILE(57),
+ GPIO_GET_VALUE_FILE(58), GPIO_GET_VALUE_FILE(59)};
+
+char *gpio_value_file_gpi_list[8] = {GPIO_GET_VALUE_FILE(44), GPIO_GET_VALUE_FILE(45),
+ GPIO_GET_VALUE_FILE(46), GPIO_GET_VALUE_FILE(47),
+ GPIO_GET_VALUE_FILE(48), GPIO_GET_VALUE_FILE(49),
+ GPIO_GET_VALUE_FILE(50), GPIO_GET_VALUE_FILE(51)};
+
+char *gpio_edge_file_gpi_list[8] = {GPIO_GET_EDGE_FILE(44), GPIO_GET_EDGE_FILE(45),
+ GPIO_GET_EDGE_FILE(46), GPIO_GET_EDGE_FILE(47),
+ GPIO_GET_EDGE_FILE(48), GPIO_GET_EDGE_FILE(49),
+ GPIO_GET_EDGE_FILE(50), GPIO_GET_EDGE_FILE(51)};
+
+char *gpo_pin_str[8] = {"52", "53", "54", "55", "56", "57", "58", "59"};
+int gpo_pin_str_len[8] = {2, 2, 2, 2, 2, 2, 2, 2};
+char *gpi_pin_str[8] = {"44", "45", "46", "47", "48", "49", "50", "51"};
+int gpi_pin_str_len[8] = {2, 2, 2, 2, 2, 2, 2, 2};
+char *gpio_pin_value_str[2] = {"0", "1"};
+int gpio_pin_value_str_len[2] = {1, 1};
+int gpo_value_fd[8] = {0};
+int gpi_value_fd[8] = {0};
+
+/**
+ * @brief determine whether the specified file exists
+ * @param file_path file path
+ * @return 1 - success, -1 - error
+ */
+int is_file_exist(const char *file_path)
+{
+ if (file_path == NULL)
+ return -1;
+ if (access(file_path, F_OK) == 0)
+ return 1;
+ return -1;
+}
+
+/**
+ * @brief Put the processed host computer data into the queue
+ * @param array Buffer pointer in the queue
+ * @param count The number of data in the buffer
+ */
+void print_array(int *array, int count)
+{
+ if (count == 0)
+ {
+ printf("[]\r\n");
+ return;
+ }
+ printf("[");
+ int i;
+ for (i = 0; i < count - 1; i++)
+ {
+ printf("%d,", array[i]);
+ }
+ printf("%d]\r\n", array[i]);
+}
diff --git a/source/gpio_common.h b/source/gpio_common.h
new file mode 100644
index 0000000..8fcc124
--- /dev/null
+++ b/source/gpio_common.h
@@ -0,0 +1,90 @@
+#ifndef __GPIO_COMMON_H
+#define __GPIO_COMMON_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define GPIO_EXPORT_PATH "/sys/class/gpio/export"
+#define GPIO_GET_PIN_STR(pin) #pin
+#define GPIO_GET_VALUE_FILE(pin) "/sys/class/gpio/gpio" #pin "/value"
+#define GPIO_GET_EDGE_FILE(pin) "/sys/class/gpio/gpio" #pin "/edge"
+#define GPIO_PINDEF_TO_INDEX(pin_t) ((int)pin_t)
+#define GPIO_VALUEDEF_TO_INDEX(value_t) ((int)value_t)
+
+#define ON_ERROR(res, message1, message2) \
+ if (res < 0) \
+ { \
+ sprintf(perror_buffer, "error %d at %s:%d, %s, %s", res, __FILE__, __LINE__, message1, message2); \
+ perror(perror_buffer); \
+ }
+
+#define ON_ERROR_RET_VOID(res, message1, message2) \
+ ON_ERROR(res, message1, message2); \
+ if (res < 0) \
+ { \
+ res = 0; \
+ return; \
+ }
+
+#define ON_ERROR_RET(res, message1, message2, retval) \
+ ON_ERROR(res, message1, message2); \
+ if (res < 0) \
+ { \
+ res = 0; \
+ return retval; \
+ }
+
+typedef enum
+{
+ GPO0 = 0,
+ GPO1 = 1,
+ GPO2 = 2,
+ GPO3 = 3,
+ GPO4 = 4,
+ GPO5 = 5,
+ GPO6 = 6,
+ GPO7 = 7
+} gpo_pin_enum_t;
+
+typedef enum
+{
+ GPI0 = 0,
+ GPI1 = 1,
+ GPI2 = 2,
+ GPI3 = 3,
+ GPI4 = 4,
+ GPI5 = 5,
+ GPI6 = 6,
+ GPI7 = 7
+} gpi_pin_enum_t;
+
+typedef enum
+{
+ GPIO_VALUE_LOW = 0,
+ GPIO_VALUE_HIGH = 1
+} gpio_value_enum_t;
+
+int is_file_exist(const char *file_path);
+extern char perror_buffer[];
+extern char *gpio_value_file_gpo_list[];
+extern char *gpio_value_file_gpi_list[];
+extern char *gpio_edge_file_gpi_list[];
+extern char *gpo_pin_str[];
+extern char *gpi_pin_str[];
+extern int gpo_pin_str_len[];
+extern int gpi_pin_str_len[];
+extern char *gpio_pin_value_str[];
+extern int gpo_value_fd[];
+extern int gpi_value_fd[];
+extern int gpio_pin_value_str_len[];
+void print_array(int *array, int count);
+
+#endif
\ No newline at end of file
diff --git a/source/host_computer.c b/source/host_computer.c
new file mode 100644
index 0000000..f910e09
--- /dev/null
+++ b/source/host_computer.c
@@ -0,0 +1,293 @@
+/**
+ * @file host_computer.c
+ * @brief Commnunicate with host computer. Protocal is described in hostcomputer通信协议.md
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2022/01/16
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/16 | 1.0 | miaow | Write this file
+ * |
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * @brief Queue handle structure
+ */
+typedef struct
+{
+ queue_uint64_msg_t *data_q; // A pointer to the queue for valve data
+ queue_uint64_msg_t *cmd_q; // A pointer to the queue for commands
+ int socket_fd; // The socket fd for receiving commands and data
+ int need_exit; // The flag variable to indicate whether to exit the loop_thread in this file
+ pthread_t loop_thread; // The main routine of this module, which parses commands and data from host, puts them into the queue
+ pthread_mutex_t loop_thread_mutex; // The mutex for loop_thread
+} hostcomputer_t;
+
+static hostcomputer_t _global_structure;
+void *loop_thread_func(void *param);
+
+/**
+ * @brief Pre initialize host computer module
+ * @param data_q A pointer to the queue storing the valve data from host computer
+ * @param cmd_q A pointer to the queue storing the cmd from host computer
+ * @return 0 - success
+ */
+int hostcomputer_init(queue_uint64_msg_t *data_q, queue_uint64_msg_t *cmd_q)
+{
+ _global_structure.data_q = data_q;
+ _global_structure.cmd_q = cmd_q;
+
+ pthread_mutex_init(&_global_structure.loop_thread_mutex, NULL);
+ pthread_create(&_global_structure.loop_thread, NULL, loop_thread_func, NULL);
+
+ return 0;
+}
+
+/**
+ * @brief Receive `size` bytes from a socket. If no more bytes are available at the socket, this function return -1 when timeout reaches.
+ * @param fd The socket fd
+ * @param buf Received bytes
+ * @param size Number of bytes to receive
+ * @return These calls return the number of bytes received, or -1 if time out occurred
+ */
+static int recvn(int fd, char *buf, int size)
+{
+ char *pt = buf;
+ int count = size;
+ while (count > 0)
+ {
+ int len = recv(fd, pt, count, 0);
+ // if (len == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
+ // {
+ // // printf("recv timeout\r\n");
+ // }
+ if (len == -1)
+ return -1;
+ else if (len == 0)
+ return size - count;
+ pt += len;
+ count -= len;
+ }
+ return size;
+}
+
+/**
+ * @brief To inspect the status of TCP connection
+ * @param sock_fd The socket
+ * @return 0 - Not connected, 1 - connected
+ */
+static int is_connected(int sock_fd)
+{
+ struct tcp_info info;
+ int len = sizeof(info);
+ getsockopt(sock_fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
+ return info.tcpi_state == TCP_ESTABLISHED;
+}
+
+/**
+ * @brief This function runs in child thread and handles communication with host computer
+ * @param param NULL
+ * @return NULL
+ */
+void *loop_thread_func(void *param)
+{
+ printf("loop thread in %s start\r\n", __FILE__);
+ int need_exit = 0;
+ char pre;
+ uint16_t n_bytes;
+ char type[2];
+ char data[99999];
+ char check[2];
+ while (!need_exit)
+ {
+ pthread_mutex_lock(&_global_structure.loop_thread_mutex);
+ need_exit = _global_structure.need_exit;
+ pthread_mutex_unlock(&_global_structure.loop_thread_mutex);
+ // reconnect if not connected
+ if (!is_connected(_global_structure.socket_fd))
+ {
+ _global_structure.socket_fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct timeval timeout = {.tv_sec = 10, .tv_usec = 0};
+ setsockopt(_global_structure.socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+ ON_ERROR_RET(_global_structure.socket_fd, "hostcomputer_init", "", NULL);
+ struct sockaddr_in serAddr;
+ serAddr.sin_family = AF_INET;
+ serAddr.sin_addr.s_addr = inet_addr(HOST_COMPUTER_IP);
+ serAddr.sin_port = htons(HOST_COMPUTER_PORT);
+ printf("Connecting host computer...");
+ fflush(stdout);
+ if (connect(_global_structure.socket_fd, (struct sockaddr *)&serAddr, sizeof(struct sockaddr_in)) == -1)
+ {
+ sleep(2);
+ close(_global_structure.socket_fd);
+ printf("FAILED\r\n");
+ continue;
+ }
+ printf("OK\r\n");
+ }
+
+ // =======================parse the protocal=========================================
+
+ if (recvn(_global_structure.socket_fd, (char *)&pre, 1) > 1)
+ {
+ // close(_global_structure.socket_fd);
+ printf("pre_len!=1\r\n");
+ continue;
+ }
+ if (pre != 0xAA)
+ {
+ // close(_global_structure.socket_fd);
+ // printf("%X ", (int)pre);
+ fflush(stdout);
+ continue;
+ }
+ if (recvn(_global_structure.socket_fd, (char *)&n_bytes, 2) != 2)
+ {
+ // close(_global_structure.socket_fd);
+ printf("n_bytes_len!=2\r\n");
+ continue;
+ }
+ n_bytes = ntohs(n_bytes);
+ if (n_bytes > 4096 || n_bytes < 2)
+ {
+ // close(_global_structure.socket_fd);
+ printf("n_bytes>4096 or n_bytes<2\r\n");
+ continue;
+ }
+ if (recvn(_global_structure.socket_fd, (char *)type, 2) != 2)
+ {
+ // close(_global_structure.socket_fd);
+ printf("type!=2\r\n");
+ continue;
+ }
+ if (recvn(_global_structure.socket_fd, (char *)data, n_bytes - 2) != n_bytes - 2)
+ {
+ // close(_global_structure.socket_fd);
+ printf("data_len!=n_bytes-2\r\n");
+ continue;
+ }
+
+ data[n_bytes - 2] = 0;
+ if (recvn(_global_structure.socket_fd, (char *)check, 2) != 2)
+ {
+ // close(_global_structure.socket_fd);
+ printf("check_len!=2\r\n");
+ continue;
+ }
+ if (recvn(_global_structure.socket_fd, (char *)&pre, 1) != 1)
+ {
+ // close(_global_structure.socket_fd);
+ printf("end_len!=1\r\n");
+ continue;
+ }
+ if (pre != 0xBB)
+ {
+ // close(_global_structure.socket_fd);
+ printf("end!=0xBB\r\n");
+ continue;
+ }
+
+ // =======================parse the commands=========================================
+ // commands are reformed as an uint64_t, 0x--------xxxxxxxx, where `-` refers its paramter and `x` is HOSTCOMPUTER_CMD
+ if (type[0] == 'd' && type[1] == 'a')
+ {
+ // printf("%dbytes of data put to data queue\r\n", (int)n_bytes - 2);
+ if (n_bytes - 2 != 6 * HOST_COMPUTER_PICTURE_ROW_NUM)
+ {
+ printf("n_bytes-2!=%d\r\n", 6 * HOST_COMPUTER_PICTURE_ROW_NUM);
+ continue;
+ }
+ int data_index = 0;
+ uint64_t tmp_one_line_data = 0;
+ // valve arange(nth in rank) 6th 5th 4th 3th 2th 1th
+ // byte arange(nth received) (6*x)th (5*x)th (4*x)th (3*x)th (2*x)th xth
+ // where x in range(500)
+ //
+
+ for (int i = 0; i < HOST_COMPUTER_PICTURE_ROW_NUM; i++)
+ {
+ tmp_one_line_data = 0ul;
+ for (int j = 0; j < 6; j++)
+ {
+ tmp_one_line_data <<= 8;
+ tmp_one_line_data |= data[data_index++];
+ }
+ queue_uint64_put(_global_structure.data_q, tmp_one_line_data);
+ }
+ }
+ else if (type[0] == 's' && type[1] == 't')
+ {
+ // printf("Start put to cmd queue, param:%d\r\n", (int)atoll(data));
+ queue_uint64_put(_global_structure.cmd_q, (atoll(data) << 32) | HOSTCOMPUTER_CMD_START);
+ }
+ else if (type[0] == 's' && type[1] == 'p')
+ {
+ // printf("Stop put to cmd queue, param:%d\r\n", (int)atoll(data));
+ queue_uint64_put(_global_structure.cmd_q, (atoll(data) << 32) | HOSTCOMPUTER_CMD_STOP);
+ }
+ else if (type[0] == 't' && type[1] == 'e')
+ {
+ // printf("Test put to cmd queue, param:%d\r\n", (int)atoll(data));
+ queue_uint64_put(_global_structure.cmd_q, (atoll(data) << 32) | HOSTCOMPUTER_CMD_TEST);
+ }
+ else if (type[0] == 'p' && type[1] == 'o')
+ {
+ // printf("Power on put to cmd queue, param:%d\r\n", (int)atoll(data));
+ queue_uint64_put(_global_structure.cmd_q, (atoll(data) << 32) | HOSTCOMPUTER_CMD_POWERON);
+ }
+ else if (type[0] == 's' && type[1] == 'c')
+ {
+ // printf("Set camera triggle pulse count put to cmd queue, param:%d\r\n", (int)atoll(data));
+ queue_uint64_put(_global_structure.cmd_q, (atoll(data) << 32) | HOSTCOMPUTER_CMD_SETCAMERATRIGPULSECOUNT);
+ }
+ else if (type[0] == 's' && type[1] == 'v')
+ {
+ // printf("Set valve pulse count put to cmd queue, param:%d\r\n", (int)atoll(data));
+ queue_uint64_put(_global_structure.cmd_q, (atoll(data) << 32) | HOSTCOMPUTER_CMD_SETVALVETRIGPULSECOUNT);
+ }
+ else if (type[0] == 's' && type[1] == 'd')
+ {
+ // printf("Set camera to valve pulse count put to cmd queue, param:%d\r\n", (int)atoll(data));
+ queue_uint64_put(_global_structure.cmd_q, (atoll(data) << 32) | HOSTCOMPUTER_CMD_SETCAMERATOVALVEPULSECOUNT);
+ }
+ else
+ {
+ printf("Unknown command received");
+ }
+ }
+ printf("loop thread in %s exit\r\n", __FILE__);
+ return NULL;
+}
+
+/**
+ * @brief Deinitialize and release resources used by host computer module
+ * @return int
+ */
+int hostcomputer_deinit()
+{
+ pthread_mutex_lock(&_global_structure.loop_thread_mutex);
+ _global_structure.need_exit = 1;
+ pthread_mutex_unlock(&_global_structure.loop_thread_mutex);
+ pthread_join(_global_structure.loop_thread, NULL);
+ pthread_mutex_destroy(&_global_structure.loop_thread_mutex);
+
+ close(_global_structure.socket_fd);
+ _global_structure.socket_fd = 0;
+ _global_structure.need_exit = 0;
+ _global_structure.cmd_q = NULL;
+ _global_structure.data_q = NULL;
+ return 0;
+}
diff --git a/source/host_computer.h b/source/host_computer.h
new file mode 100644
index 0000000..19fbaf5
--- /dev/null
+++ b/source/host_computer.h
@@ -0,0 +1,45 @@
+/**
+ * @file host_computer.h
+ * @brief Commnunicate with host computer. Protocal is described in hostcomputer通信协议.md
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2022/01/16
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/16 | 1.0 | miaow | Write this file
+ * |
+ */
+#ifndef __HOST_COMPUTER_H
+#define __HOST_COMPUTER_H
+
+#include
+#include
+#include
+
+#define HOST_COMPUTER_IP "192.168.2.10"
+#define HOST_COMPUTER_PORT 13452
+#define HOST_COMPUTER_PICTURE_ROW_NUM 500
+
+/**
+ * @brief The commonds, ref hostcomputer通信协议.md
+ */
+enum HOSTCOMPUTER_CMD
+{
+ HOSTCOMPUTER_CMD_START = 2,
+ HOSTCOMPUTER_CMD_STOP = 3,
+ HOSTCOMPUTER_CMD_TEST = 4,
+ HOSTCOMPUTER_CMD_POWERON = 5,
+ HOSTCOMPUTER_CMD_SETCAMERATRIGPULSECOUNT = 6,
+ HOSTCOMPUTER_CMD_SETVALVETRIGPULSECOUNT = 7,
+ HOSTCOMPUTER_CMD_SETCAMERATOVALVEPULSECOUNT = 8
+
+};
+
+int hostcomputer_init(queue_uint64_msg_t *data_q, queue_uint64_msg_t *cmd_q);
+int hostcomputer_deinit(void);
+
+#endif
diff --git a/source/main.c b/source/main.c
new file mode 100644
index 0000000..8e68429
--- /dev/null
+++ b/source/main.c
@@ -0,0 +1,244 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * @brief Value of state machine
+ */
+typedef enum
+{
+ NOT_INITIALIZED = 0,
+ INITIALIZED = 1,
+ RUNNING = 2,
+ SLEEPING = 3,
+ STOPPED = 4
+} status_enum_t;
+
+valvedata_t valvedata = {0};
+queue_uint64_msg_t data_queue = {0};
+queue_uint64_msg_t cmd_queue = {0};
+
+static int count_valve = 1, count_camera = 0, count_valve_should_be = 2;
+static uint64_t count_continues = 0UL, count_valve_continues = 0UL, count_camera_continues = 0UL;
+static status_enum_t status = NOT_INITIALIZED;
+static int camera_trigger_pulse_count = 0;
+static int valve_should_trigger_pulse_count = 0;
+static int valve_trigger_pulse_count = 0;
+static int camera_to_valve_pulse_count = 0;
+
+#define ROTATE_UINT64_RIGHT(x, n) ((x) >> (n)) | ((x) << ((64) - (n)))
+#define ROTATE_UINT64_LEFT(x, n) ((x) << (n)) | ((x) >> ((64) - (n)))
+
+void on_encoder(void);
+void valve_test(float ms_for_each_channel);
+void valve_test2(float ms_for_each_channel, int which_channel);
+void valve_test3(float ms_for_each_channel);
+void process_cmd(uint64_t *cmd);
+
+int main(int argc, char *argv[])
+{
+ queue_uint64_init(&data_queue, 99999);
+ queue_uint64_init(&cmd_queue, 99999);
+
+ // valve_init();
+ // printf("testing valve.....");
+ // fflush(stdout);
+ // valve_test3(100.0f);
+ // valve_test2(200.0f, 0);
+ // valve_test(200.0f);
+ // printf("OK\r\n");
+ // valve_deinit();
+
+ hostcomputer_init(&data_queue, &cmd_queue);
+ uint64_t cmd;
+ int TRUE = 1;
+ while (TRUE)
+ {
+ if (queue_uint64_get(&cmd_queue, &cmd) == 0)
+ {
+ process_cmd(&cmd);
+ usleep(100000);
+ }
+ }
+ hostcomputer_deinit();
+ queue_uint64_deinit(&data_queue);
+ queue_uint64_deinit(&cmd_queue);
+ return 0;
+}
+
+void process_cmd(uint64_t *cmd)
+{
+ int tmp_cmd = (int)*cmd;
+ int tmp_data = (int)(*cmd >> 32);
+ if (status == SLEEPING)
+ {
+ if (tmp_cmd == HOSTCOMPUTER_CMD_START)
+ {
+ valve_should_trigger_pulse_count = camera_trigger_pulse_count / HOST_COMPUTER_PICTURE_ROW_NUM;
+ for (int i = 0; i < camera_to_valve_pulse_count * HOST_COMPUTER_PICTURE_ROW_NUM / camera_trigger_pulse_count; i++)
+ queue_uint64_put(&data_queue, 0);
+ valve_init();
+ cameratrigger_init();
+ encoder_init(on_encoder);
+ status = RUNNING;
+ printf("\r\n>>>>>\r\nstatus==RUNNING\r\n<<<<<\r\n\r\n");
+ }
+ else if (tmp_cmd == HOSTCOMPUTER_CMD_TEST)
+ {
+ valve_init();
+ valve_test(500.0f);
+ valve_deinit();
+ }
+ }
+ else if (status == NOT_INITIALIZED)
+ {
+ if (tmp_cmd == HOSTCOMPUTER_CMD_SETCAMERATRIGPULSECOUNT)
+ {
+ camera_trigger_pulse_count = tmp_data;
+ }
+ else if (tmp_cmd == HOSTCOMPUTER_CMD_SETVALVETRIGPULSECOUNT)
+ {
+ valve_trigger_pulse_count = tmp_data;
+ }
+ else if (tmp_cmd == HOSTCOMPUTER_CMD_SETCAMERATOVALVEPULSECOUNT)
+ {
+ camera_to_valve_pulse_count = tmp_data;
+ }
+ else if (tmp_cmd == HOSTCOMPUTER_CMD_TEST)
+ {
+ valve_init();
+ valve_test(500.0f);
+ valve_deinit();
+ }
+ if (camera_trigger_pulse_count != 0 && valve_trigger_pulse_count != 0 && camera_to_valve_pulse_count != 0)
+ {
+ status = INITIALIZED;
+ printf("\r\n>>>>>\r\nstatus==INITIALIZED\r\ncamera_trigger_pulse_count=%d\r\nvalve_trigger_pulse_count=%d\r\ncamera_to_valve_pulse_count=%d\r\n<<<<<\r\n\r\n", camera_trigger_pulse_count, valve_trigger_pulse_count, camera_to_valve_pulse_count);
+ }
+ }
+ else if (status == INITIALIZED)
+ {
+ if (tmp_cmd == HOSTCOMPUTER_CMD_START)
+ {
+ valve_should_trigger_pulse_count = camera_trigger_pulse_count / HOST_COMPUTER_PICTURE_ROW_NUM;
+ printf("valve_should_trigger_pulse_count=%d", valve_should_trigger_pulse_count);
+ for (int i = 0; i < camera_to_valve_pulse_count * HOST_COMPUTER_PICTURE_ROW_NUM / camera_trigger_pulse_count; i++)
+ queue_uint64_put(&data_queue, 0);
+ valve_init();
+ cameratrigger_init();
+ encoder_init(on_encoder);
+ status = RUNNING;
+ printf("\r\n>>>>>\r\nstatus==RUNNING\r\n<<<<<\r\n\r\n");
+ }
+ else if (tmp_cmd == HOSTCOMPUTER_CMD_TEST)
+ {
+ valve_init();
+ valve_test(500.0f);
+ valve_deinit();
+ }
+ }
+ else if (status == RUNNING)
+ {
+ if (tmp_cmd == HOSTCOMPUTER_CMD_STOP)
+ {
+ encoder_deinit();
+ cameratrigger_deinit();
+ valve_deinit();
+ queue_uint64_clear(&data_queue);
+ status = SLEEPING;
+ printf("\r\n>>>>>\r\nstatus==SLEEPING\r\n<<<<<\r\n\r\n");
+ }
+ }
+}
+
+void valve_test(float ms_for_each_channel)
+{
+ uint64_t valve_data = 1ul;
+ for (int i = 0; i < 48; i++)
+ {
+ usleep((useconds_t)(ms_for_each_channel * 500.0f));
+ valvedata.valvedata_1 = valve_data << i;
+ valve_sendmsg(&valvedata);
+ usleep((useconds_t)(ms_for_each_channel * 500.0f));
+ valvedata.valvedata_1 = 0;
+ valve_sendmsg(&valvedata);
+ }
+}
+
+void valve_test2(float ms_for_each_channel, int which_channel)
+{
+ uint64_t valve_data = 1ul;
+ for (int i = 0; i < 10; i++)
+ {
+ usleep((useconds_t)(ms_for_each_channel * 500.0f));
+ valvedata.valvedata_1 = valve_data << which_channel;
+ valve_sendmsg(&valvedata);
+ usleep((useconds_t)(ms_for_each_channel * 500.0f));
+ valvedata.valvedata_1 = 0;
+ valve_sendmsg(&valvedata);
+ }
+}
+
+void valve_test3(float ms_for_each_channel)
+{
+ valvedata.valvedata_1 = 0x5555555555555555ul;
+ for (int i = 0; i < 9999; i++)
+ {
+ usleep((useconds_t)(ms_for_each_channel * 250.0f));
+ valvedata.valvedata_1 = 0x5555555555555555ul;
+ valve_sendmsg(&valvedata);
+ usleep((useconds_t)(ms_for_each_channel * 250.0f));
+
+ valvedata.valvedata_1 = 0;
+ valve_sendmsg(&valvedata);
+ usleep((useconds_t)(ms_for_each_channel * 250.0f));
+
+ valvedata.valvedata_1 = 0xaaaaaaaaaaaaaaaaul;
+ valve_sendmsg(&valvedata);
+ usleep((useconds_t)(ms_for_each_channel * 250.0f));
+
+ valvedata.valvedata_1 = 0;
+ valve_sendmsg(&valvedata);
+ usleep((useconds_t)(ms_for_each_channel * 250.0f));
+ }
+}
+
+void on_encoder()
+{
+ count_continues++;
+
+ if (++count_valve == valve_trigger_pulse_count + 1)
+ {
+ count_valve = 1;
+ count_valve_continues++;
+ valve_sendmsg(&valvedata);
+
+ // printf("data:%llx send to valve, queue length is %d\r\n", valvedata.valvedata_1, data_queue.nData);
+ // printf("%016llx ", valvedata.valvedata_1);
+ fflush(stdout);
+ }
+
+ if (++count_valve_should_be == valve_should_trigger_pulse_count + 2)
+ {
+ count_valve_should_be = 2;
+ valvedata.valvedata_1 = 0;
+ queue_uint64_get(&data_queue, &(valvedata.valvedata_1));
+ // if (data_queue.nData == 0)
+ // {
+ // printf("sb\r\n");
+ // }
+ }
+
+ if (++count_camera == camera_trigger_pulse_count)
+ {
+ // printf("camera triggled\r\n");
+ count_camera = 0;
+ count_camera_continues++;
+ cameratrigger_trig();
+ }
+}
diff --git a/source/main.h b/source/main.h
new file mode 100644
index 0000000..7ecc416
--- /dev/null
+++ b/source/main.h
@@ -0,0 +1,6 @@
+#ifndef __MAIN_H
+#define __MAIN_H
+
+
+
+#endif
diff --git a/source/queue_reference.c b/source/queue_reference.c
new file mode 100644
index 0000000..902a536
--- /dev/null
+++ b/source/queue_reference.c
@@ -0,0 +1,167 @@
+/**
+ * @file queue_reference.c
+ * @brief Thread safe queue, which stores void* pointers
+ * @details Call queue_init(queue_reference_msg_t *q, int max_count) paired with queue_deinit(queue_reference_msg_t *q) as their names imply, queue_initstruct(queue_reference_msg_t *q)Initialize the message queue structure,*queue_get(queue_reference_msg_t *q) and queue_put(queue_reference_msg_t *q, void *data) In and out of the team operation
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2021/12/25 merry christmas
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/09 | 1.0 | miaow | Write this file
+ * |
+ */
+
+#include
+#include
+#ifdef QUEUE_REFERENCE_DEBUG
+#include
+#include
+#endif
+#include
+
+/**
+ * @brief Take out the first item from the circular queue
+ * @param q The queue handler
+ * @return A pointer to the item
+ */
+void *queue_reference_get(queue_reference_msg_t *q)
+{
+ void *data = NULL;
+ pthread_mutex_lock(&q->_mux);
+ // while (q->lget == q->lput && 0 == q->nData)
+ // {
+ // // The reason program goes here: assuming there are 2 consmer threads block in this function
+ // // One wakes first and consumes 2 data quickly before another wakes
+ // // In the circumstances that the queue contains 2 items formerly, the second thread should not get data from an empty queue
+ // // This may happen when 2 queue_puts was called by producers and at that moment 2 consmer threads have been blocked
+
+ // // It is designed as a circular queue, where lget==lput means:
+ // // 1:nData!=0,a full queue
+ // // 2:nData为0,an empty queue
+ // q->nEmptyThread++;
+ // pthread_cond_wait(&q->_cond_get, &q->_mux);
+ // q->nEmptyThread--;
+ // }
+ if (q->nData == 0)
+ {
+ pthread_mutex_unlock(&q->_mux);
+ return NULL;
+ }
+#ifdef QUEUE_REFERENCE_DEBUG
+ printf("get data! lget:%d, ", q->lget);
+#endif
+ data = (q->buffer)[q->lget++];
+#ifdef QUEUE_REFERENCE_DEBUG
+ printf("data:% lld", *((uint64_t *)data));
+#endif
+ if (q->lget == q->size)
+ {
+ // this is a circular queue
+ q->lget = 0;
+ }
+ q->nData--;
+#ifdef QUEUE_REFERENCE_DEBUG
+ printf(", nData:%d\r\n", q->nData);
+#endif
+ // if (q->nFullThread)
+ // {
+ // // call pthread_cond_signal only when necessary, enter the kernel state as little as possible
+ // pthread_cond_signal(&q->_cond_put);
+ // }
+ pthread_mutex_unlock(&q->_mux);
+ return data;
+}
+
+/**
+ * @brief Initialize the queue with a size (maximum count of items) specified in q->size
+ * @param q The queue hander to be initialized
+ * @return 0 - success, -1 - failed
+ * @note q->size should be set before calling this function
+ */
+int queue_reference_initstruct(queue_reference_msg_t *q)
+{
+ q->buffer = malloc(q->size * sizeof(void *));
+ if (q->buffer == NULL)
+ return -1;
+ pthread_mutex_init(&q->_mux, NULL);
+ // pthread_cond_init(&q->_cond_get, NULL);
+ // pthread_cond_init(&q->_cond_put, NULL);
+ return 0;
+}
+
+/**
+ * @brief Initialize the queue
+ * @param q The queue hander to be initialized
+ * @param max_count Maximum count of items in the queue
+ * @return 0 - success, -1 - failed
+ */
+int queue_reference_init(queue_reference_msg_t *q, int max_count)
+{
+ q->size = max_count;
+ return queue_reference_initstruct(q);
+}
+
+/**
+ * @brief Deinitialize the queue
+ * @param q The queue handle
+ * @return 0 - success
+ */
+int queue_reference_deinit(queue_reference_msg_t *q)
+{
+ free(q->buffer);
+ q->buffer = NULL;
+ pthread_mutex_destroy(&q->_mux);
+ // pthread_cond_destroy(&q->_cond_get);
+ // pthread_cond_destroy(&q->_cond_put);
+ q->size = 0;
+ q->nData = 0;
+ q->lget = 0;
+ q->lput = 0;
+ // q->nEmptyThread = 0;
+ // q->nFullThread = 0;
+ return 0;
+}
+
+/**
+ * @brief Put one item into the circular queue
+ * @param q The queue handle
+ * @param data A pointer to the item
+ * @return 0 - success, -1 - failed
+ */
+int queue_reference_put(queue_reference_msg_t *q, void *data)
+{
+ pthread_mutex_lock(&q->_mux);
+ // while (q->lget == q->lput && q->nData)
+ // {
+ // q->nFullThread++;
+ // pthread_cond_wait(&q->_cond_put, &q->_mux);
+ // q->nFullThread--;
+ // }
+ if (q->lget == q->lput && q->nData)
+ {
+ pthread_mutex_unlock(&q->_mux);
+ return -1;
+ }
+#ifdef QUEUE_REFERENCE_DEBUG
+ printf("put data! lput:%d, data:%lld", q->lput, *((uint64_t *)data));
+#endif
+ (q->buffer)[q->lput++] = data;
+ if (q->lput == q->size)
+ {
+ q->lput = 0;
+ }
+ q->nData++;
+#ifdef QUEUE_REFERENCE_DEBUG
+ printf(" nData:%d\n", q->nData);
+#endif
+ // if (q->nEmptyThread)
+ // {
+ // pthread_cond_signal(&q->_cond_get);
+ // }
+ pthread_mutex_unlock(&q->_mux);
+ return 0;
+}
\ No newline at end of file
diff --git a/source/queue_reference.h b/source/queue_reference.h
new file mode 100644
index 0000000..958450a
--- /dev/null
+++ b/source/queue_reference.h
@@ -0,0 +1,46 @@
+/**
+ * @file queue_reference.h
+ * @brief Thread safe queue, which stores void pointers
+ * @details Call queue_init(queue_reference_msg_t *q, int max_count) paired with queue_deinit(queue_reference_msg_t *q) as their names imply, queue_initstruct(queue_reference_msg_t *q)Initialize the message queue structure,*queue_get(queue_reference_msg_t *q) and queue_put(queue_reference_msg_t *q, void *data) In and out of the team operation
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2021/12/25 merry christmas
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/09 | 1.0 | miaow | Write this file
+ * |
+ */
+#if !defined(__QUEUE_REFERENCE_H)
+#define __QUEUE_REFERENCE_H
+
+#include
+
+/**
+ * @brief Queue handle structure
+ */
+typedef struct
+{
+ void **buffer; // 缓冲数据, .buffer = msg
+ int size; // 队列大小,使用的时候给出稍大的size,可以减少进入内核态的操作
+ int lget; // 取队列数据的偏移量
+ int lput; // 放队列数据的偏移量
+ int nData; // 队列中数据的个数,用来判断队列满/空
+ // int nFullThread; // 由于队列满而阻塞在put_queue的线程个数
+ // int nEmptyThread; // 由于队列空而阻塞在get_queue的线程个数
+ pthread_mutex_t _mux;
+ // pthread_cond_t _cond_get, _cond_put;
+} queue_reference_msg_t;
+
+// #define QUEUE_REFERENCE_DEBUG
+
+void *queue_reference_get(queue_reference_msg_t *q);
+int queue_reference_put(queue_reference_msg_t *q, void *data);
+int queue_reference_initstruct(queue_reference_msg_t *q);
+int queue_reference_init(queue_reference_msg_t *q, int max_count);
+int queue_reference_deinit(queue_reference_msg_t *q);
+
+#endif // __QUEUE_REFERENCE_H
diff --git a/source/queue_uint64.c b/source/queue_uint64.c
new file mode 100644
index 0000000..7ad5d0a
--- /dev/null
+++ b/source/queue_uint64.c
@@ -0,0 +1,195 @@
+
+/**
+ * @file queue_uint64.c
+ * @brief Thread safe queue, which stores uint64_t
+ * @details Call queue_init(queue_uint64_msg_t *q, int max_count) paired with queue_deinit(queue_uint64_msg_t *q) as their names imply, queue_initstruct(queue_uint64_msg_t *q)Initialize the message queue structure,*queue_get(queue_uint64_msg_t *q) and queue_put(queue_uint64_msg_t *q, void *data) In and out of the team operation
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2021/01/10
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/09 | 1.0 | miaow | Write this file
+ * |
+ */
+
+#include
+#include
+#ifdef QUEUE_UINT64_DEBUG
+#include
+#include
+#endif
+#include
+
+/**
+ * @brief Take out the first item from the circular queue
+ * @param q The queue handler
+ * @param data A buffer of uint64_t[1] to store the data taken out
+ * @return 0 - success, -1 - failed
+ */
+int queue_uint64_get(queue_uint64_msg_t *q, uint64_t *data)
+{
+ pthread_mutex_lock(&q->_mux);
+ // while (q->lget == q->lput && 0 == q->nData)
+ // {
+ // // The reason program goes here: assuming there are 2 consmer threads block in this function
+ // // One wakes first and consumes 2 data quickly before another wakes
+ // // In the circumstances that the queue contains 2 items formerly, the second thread should not get data from an empty queue
+ // // This may happen when 2 queue_puts was called by producers and at that moment 2 consmer threads have been blocked
+
+ // // It is designed as a circular queue, where lget==lput means:
+ // // 1:nData!=0,a full queue
+ // // 2:nData为0,an empty queue
+ // q->nEmptyThread++;
+ // pthread_cond_wait(&q->_cond_get, &q->_mux);
+ // q->nEmptyThread--;
+ // }
+ if (q->nData == 0)
+ {
+ pthread_mutex_unlock(&q->_mux);
+ return -1;
+ }
+#ifdef QUEUE_UINT64_DEBUG
+ printf("get data! lget:%d, ", q->lget);
+#endif
+ *data = (q->buffer)[q->lget++];
+#ifdef QUEUE_UINT64_DEBUG
+ printf("data:% lld", *data);
+#endif
+ if (q->lget == q->size)
+ {
+ // this is a circular queue
+ q->lget = 0;
+ }
+ q->nData--;
+#ifdef QUEUE_UINT64_DEBUG
+ printf(", nData:%d\r\n", q->nData);
+#endif
+ // if (q->nFullThread)
+ // {
+ // // call pthread_cond_signal only when necessary, enter the kernel state as little as possible
+ // pthread_cond_signal(&q->_cond_put);
+ // }
+ pthread_mutex_unlock(&q->_mux);
+ return 0;
+}
+
+/**
+ * @brief Initialize the queue with a size (maximum count of items) specified in q->size
+ * @param q The queue hander to be initialized
+ * @return 0 - success, -1 - failed
+ * @note q->size should be set before calling this function
+ */
+int queue_uint64_initstruct(queue_uint64_msg_t *q)
+{
+ q->buffer = malloc(q->size * sizeof(uint64_t));
+ if (q->buffer == NULL)
+ return -1;
+ pthread_mutex_init(&q->_mux, NULL);
+ // pthread_cond_init(&q->_cond_get, NULL);
+ // pthread_cond_init(&q->_cond_put, NULL);
+ return 0;
+}
+
+/**
+ * @brief Initialize the queue
+ * @param q The queue hander to be initialized
+ * @param max_count Maximum count of items in the queue
+ * @return 0 - success, -1 - failed
+ */
+int queue_uint64_init(queue_uint64_msg_t *q, int max_count)
+{
+ q->size = max_count;
+ return queue_uint64_initstruct(q);
+}
+
+/**
+ * @brief Deinitialize the queue
+ * @param q The queue handle
+ * @return 0 - success
+ */
+int queue_uint64_deinit(queue_uint64_msg_t *q)
+{
+ free(q->buffer);
+ q->buffer = NULL;
+ pthread_mutex_destroy(&q->_mux);
+ // pthread_cond_destroy(&q->_cond_get);
+ // pthread_cond_destroy(&q->_cond_put);
+ q->size = 0;
+ q->nData = 0;
+ q->lget = 0;
+ q->lput = 0;
+ // q->nEmptyThread = 0;
+ // q->nFullThread = 0;
+ return 0;
+}
+
+/**
+ * @brief Put one item into the circular queue
+ * @param q The queue handle
+ * @param data The item to put
+ * @return 0 - success, -1 - failed
+ */
+int queue_uint64_put(queue_uint64_msg_t *q, uint64_t data)
+{
+ pthread_mutex_lock(&q->_mux);
+ // while (q->lget == q->lput && q->nData)
+ // {
+ // q->nFullThread++;
+ // pthread_cond_wait(&q->_cond_put, &q->_mux);
+ // q->nFullThread--;
+ // }
+ if (q->lget == q->lput && q->nData)
+ {
+ pthread_mutex_unlock(&q->_mux);
+ return -1;
+ }
+#ifdef QUEUE_UINT64_DEBUG
+ printf("put data! lput:%d, data:%lld", q->lput, data);
+#endif
+ (q->buffer)[q->lput++] = data;
+ if (q->lput == q->size)
+ {
+ q->lput = 0;
+ }
+ q->nData++;
+#ifdef QUEUE_UINT64_DEBUG
+ printf(" nData:%d\n", q->nData);
+#endif
+ // if (q->nEmptyThread)
+ // {
+ // pthread_cond_signal(&q->_cond_get);
+ // }
+ pthread_mutex_unlock(&q->_mux);
+ return 0;
+}
+
+/**
+ * @brief Clear the circular queue
+ * @param q The queue handle
+ * @return 0 - success, -1 - failed
+ */
+int queue_uint64_clear(queue_uint64_msg_t *q)
+{
+ pthread_mutex_lock(&q->_mux);
+ // while (q->lget == q->lput && q->nData)
+ // {
+ // q->nFullThread++;
+ // pthread_cond_wait(&q->_cond_put, &q->_mux);
+ // q->nFullThread--;
+ // }
+ q->lget = q->lput = q->nData = 0;
+#ifdef QUEUE_UINT64_DEBUG
+ printf("clear!\r\n");
+#endif
+
+ // if (q->nEmptyThread)
+ // {
+ // pthread_cond_signal(&q->_cond_get);
+ // }
+ pthread_mutex_unlock(&q->_mux);
+ return 0;
+}
\ No newline at end of file
diff --git a/source/queue_uint64.h b/source/queue_uint64.h
new file mode 100644
index 0000000..80eed69
--- /dev/null
+++ b/source/queue_uint64.h
@@ -0,0 +1,48 @@
+/**
+ * @file queue_uint64.h
+ * @brief Thread safe queue, which stores uint64_t
+ * @details Call queue_init(queue_uint64_msg_t *q, int max_count) paired with queue_deinit(queue_uint64_msg_t *q) as their names imply, queue_initstruct(queue_uint64_msg_t *q)Initialize the message queue structure,*queue_get(queue_uint64_msg_t *q) and queue_put(queue_uint64_msg_t *q, void *data) In and out of the team operation
+ * @author miaow (3703781@qq.com)
+ * @version 1.0
+ * @date 2021/01/10
+ *
+ * @copyright Copyright (c) 2022 miaow
+ *
+ * @par Changelog:
+ *
+ * | Date | Version | Author | Description
+ * |
|---|
| 2022/01/09 | 1.0 | miaow | Write this file
+ * |
+ */
+#if !defined(__QUEUE_UINT64_H)
+#define __QUEUE_UINT64_H
+
+#include
+#include
+
+/**
+ * @brief Queue handle structure
+ */
+typedef struct
+{
+ uint64_t *buffer; // 缓冲数据, .buffer = msg
+ int size; // 队列大小,使用的时候给出稍大的size,可以减少进入内核态的操作
+ int lget; // 取队列数据的偏移量
+ int lput; // 放队列数据的偏移量
+ int nData; // 队列中数据的个数,用来判断队列满/空
+ // int nFullThread; // 由于队列满而阻塞在put_queue的线程个数
+ // int nEmptyThread; // 由于队列空而阻塞在get_queue的线程个数
+ pthread_mutex_t _mux;
+ // pthread_cond_t _cond_get, _cond_put;
+} queue_uint64_msg_t;
+
+// #define QUEUE_UINT64_DEBUG
+
+int queue_uint64_get(queue_uint64_msg_t *q, uint64_t *data);
+int queue_uint64_put(queue_uint64_msg_t *q, uint64_t data);
+int queue_uint64_initstruct(queue_uint64_msg_t *q);
+int queue_uint64_init(queue_uint64_msg_t *q, int max_count);
+int queue_uint64_clear(queue_uint64_msg_t *q);
+int queue_uint64_deinit(queue_uint64_msg_t *q);
+
+#endif // __QUEUE_UINT64_H
diff --git a/source/valve.c b/source/valve.c
new file mode 100644
index 0000000..260132c
--- /dev/null
+++ b/source/valve.c
@@ -0,0 +1,212 @@
+/**
+ * @file valve.c
+ * @brief Operate the valveboard with Linux application
+ * @details Call valve_init() paired with valve_deinit() as their names imply, valve_send() can be executed several times to operate up to 6 valveboards between valve_init() and valve_deinit()
+ * @mainpage github.com/NanjingForestryUniversity
+ * @author miaow
+ * @email 3703781@qq.com
+ * @version v0.9.0
+ * @date 2021/12/25 merry christmas
+ */
+
+#include
+#include
+#include
+#include
+
+
+// Write to the file desc (global variable `gpo_value_fd` in gpio_common.c) to operate a gpio.
+// So gpo_value_fd should be initialized in valve_init with great care.
+// Also, gpo_value_fd/gpi_value_fd is used in other .c files (read pluse of encoder, etc).
+#define __GPO_SET_BIT(pin_t) __GPO_SET(pin_t, GPIO_VALUE_HIGH)
+#define __GPO_CLR_BIT(pin_t) __GPO_SET(pin_t, GPIO_VALUE_LOW)
+#define __GPO_SET(pin_t, value_t) write(gpo_value_fd[GPIO_PINDEF_TO_INDEX(pin_t)], gpio_pin_value_str[GPIO_VALUEDEF_TO_INDEX(value_t)], gpio_pin_value_str_len[GPIO_VALUEDEF_TO_INDEX(value_t)])
+
+typedef struct
+{
+ int need_send; // Set this variable to 1 will cause a packet of sending
+ pthread_mutex_t need_send_mutex;
+ uint64_t data[6]; // Encoded data for sending
+ pthread_mutex_t data_mutex; // don't use, use need_send_mutex instead
+ int need_exit; // loop_thread joins to parent-thread at need_exit==1
+ pthread_mutex_t need_exit_mutex; // don't use, use need_send_mutex instead
+ pthread_t loop_thread; // The sending thread
+ pthread_cond_t is_sending;
+} valve_global_t;
+
+static valve_global_t _global_structure;
+valve_pin_enum_t valveboard_x_sdata[] = {VALVE_SDATA_1, VALVE_SDATA_2, VALVE_SDATA_3, VALVE_SDATA_4, VALVE_SDATA_5, VALVE_SDATA_6};
+
+static const int _delay = 1000 / SCLK_FREQUENCE_KHZ + 1;
+static const int _delay_on_2 = 500 / SCLK_FREQUENCE_KHZ + 1;
+
+extern int delay_us(int us);
+static void *loop_thread_func(void *param);
+
+/**
+ * @brief Initialize valve-related gpos and start loop_thread which keeps communicating with valveboards, SEN/SCLK/SDATA1/SDATA2/SDATA3/SDATA4/SDATA5/SDATA6
+ * @return 0 - success, -1 - error
+ */
+int valve_init()
+{
+ //打开GPIO
+ int fd_export = open(GPIO_EXPORT_PATH, O_WRONLY);
+ ON_ERROR_RET(fd_export, GPIO_EXPORT_PATH, "export in valve_init()", -1);
+ for (int i = 0; i < 6; i++)
+ {
+ if (is_file_exist(gpio_value_file_gpo_list[i]))
+ continue;
+ int ret = write(fd_export, gpo_pin_str[i], gpo_pin_str_len[i]);
+ ON_ERROR_RET(ret, gpo_pin_str[i], "open value file in valve_init()", -1);
+ }
+ for (int i = 0; i < 6; i++)
+ {
+ gpo_value_fd[i] = open(gpio_value_file_gpo_list[i], O_RDWR);
+ ON_ERROR_RET(gpo_value_fd[i], gpio_value_file_gpo_list[i], "open value file in valve_init()", -1);
+ }
+
+ close(fd_export);
+ pthread_mutex_init(&_global_structure.need_send_mutex, NULL);
+ pthread_mutex_init(&_global_structure.data_mutex, NULL);
+ pthread_mutex_init(&_global_structure.need_exit_mutex, NULL);
+ pthread_cond_init(&_global_structure.is_sending, NULL);
+
+ int ret = pthread_create(&_global_structure.loop_thread, NULL, loop_thread_func, NULL);
+ ON_ERROR_RET(ret, "thread create error in valve_init()", "", -1);
+
+ return 0;
+}
+
+/**
+ * @brief This function runs in child thread and handles communication with valveboard
+ */
+void *loop_thread_func(void *param)
+{
+ printf("loop_thread in %s start\r\n", __FILE__);
+ int need_exit = 0;
+ while (!need_exit)
+ {
+ pthread_mutex_lock(&_global_structure.need_send_mutex);
+
+
+ if (_global_structure.need_send == 0)
+ {
+ __GPO_CLR_BIT(VALVE_SCLK);
+ delay_us(_delay);
+ __GPO_SET_BIT(VALVE_SCLK);
+ delay_us(_delay);
+ }
+ else
+ {
+ int i = 48;
+ delay_us(_delay_on_2);
+ __GPO_SET_BIT(VALVE_SEN);
+ while (i--)
+ {
+ __GPO_CLR_BIT(VALVE_SCLK);
+ delay_us(_delay_on_2);
+ __GPO_SET(VALVE_SDATA_1, (_global_structure.data[0] & 1UL));
+ __GPO_SET(VALVE_SDATA_2, (_global_structure.data[1] & 1UL));
+ __GPO_SET(VALVE_SDATA_3, (_global_structure.data[2] & 1UL));
+ __GPO_SET(VALVE_SDATA_4, (_global_structure.data[3] & 1UL));
+ // __GPO_SET(VALVE_SDATA_5, (_global_structure.data[4] & 1UL));
+ // __GPO_SET(VALVE_SDATA_6, (_global_structure.data[5] & 1UL));
+ _global_structure.data[0] >>= 1;
+ _global_structure.data[1] >>= 1;
+ _global_structure.data[2] >>= 1;
+ _global_structure.data[3] >>= 1;
+ // _global_structure.data[4] >>= 1;
+ // _global_structure.data[5] >>= 1;
+ delay_us(_delay_on_2);
+ __GPO_SET_BIT(VALVE_SCLK);
+ delay_us(_delay);
+ }
+ __GPO_CLR_BIT(VALVE_SEN);
+ _global_structure.need_send = 0;
+ pthread_cond_signal(&_global_structure.is_sending);
+ }
+
+ // pthread_mutex_lock(&_global_structure.need_exit_mutex);
+ need_exit = _global_structure.need_exit;
+ // pthread_mutex_unlock(&_global_structure.need_exit_mutex);
+
+ pthread_mutex_unlock(&_global_structure.need_send_mutex);
+ }
+ printf("loop_thread in %s exit\r\n", __FILE__);
+ return NULL;
+}
+
+/**
+ * @brief Set valve value in forms of array.
+ * @param valve_data An array with size of 6,
+ * for example, valve_data[0]=64'h0000_FFFF_FFFF_FFFF represents the first valveboard all on
+ * valve_data[5]=64'h0000_0000_0000_0001 represents the last valveboard turn on its first valve
+ * @return 0 - success, -1 - error
+ */
+int valve_send(uint64_t *valve_data)
+{
+ pthread_mutex_lock(&_global_structure.need_send_mutex);
+ while (_global_structure.need_send == 1)
+ pthread_cond_wait(&_global_structure.is_sending, &_global_structure.need_send_mutex);
+
+ for (int i = 0; i < 6; i++)
+ {
+ _global_structure.data[i] = ~valve_data[i]; // 1 represents on in parameter of this function while off when putting data on the bus
+ }
+ _global_structure.need_send = 1; // Set this variable to 1 will cause a sending packet
+ pthread_mutex_unlock(&_global_structure.need_send_mutex);
+ return 0;
+}
+
+/**
+ * @brief Set valve value in forms of struct.
+ * @param valve_data the valve_data struct
+ * @return 0 - success, -1 - error
+ */
+int valve_sendmsg(valvedata_t *valve_data)
+{
+ pthread_mutex_lock(&_global_structure.need_send_mutex);
+ while (_global_structure.need_send == 1)
+ pthread_cond_wait(&_global_structure.is_sending, &_global_structure.need_send_mutex);
+
+ _global_structure.data[0] = ~valve_data->valvedata_1; // 1 represents on in parameter of this function while off when putting data on the bus
+ _global_structure.data[1] = ~valve_data->valvedata_2;
+ _global_structure.data[2] = ~valve_data->valvedata_3;
+ _global_structure.data[3] = ~valve_data->valvedata_4;
+ _global_structure.data[4] = ~valve_data->valvedata_5;
+ _global_structure.data[5] = ~valve_data->valvedata_6;
+
+ _global_structure.need_send = 1; // Set this variable to 1 will cause a sending packet
+ pthread_mutex_unlock(&_global_structure.need_send_mutex);
+ return 0;
+}
+
+/**
+ * @brief Deinitialize and turn off all the valve.
+ * @note This function DOES BLOCKS 100000 us at least and DOES NOT UNEXPORT gpos
+ * @return 0 - success, -1 - error
+ */
+int valve_deinit()
+{
+ uint64_t tmp[6] = {0};
+ valve_send(tmp);
+ usleep(100000);
+ pthread_mutex_lock(&_global_structure.need_send_mutex);
+ _global_structure.need_exit = 1;
+ pthread_mutex_unlock(&_global_structure.need_send_mutex);
+ pthread_join(_global_structure.loop_thread, NULL);
+ pthread_mutex_destroy(&_global_structure.need_exit_mutex);
+ pthread_mutex_destroy(&_global_structure.need_send_mutex);
+ pthread_mutex_destroy(&_global_structure.data_mutex);
+ pthread_cond_destroy(&_global_structure.is_sending);
+ memset((void *)_global_structure.data, 0, sizeof(_global_structure.data));
+ _global_structure.need_exit = 0;
+ _global_structure.need_send = 0;
+
+ for (int i = 0; i < 6; i++)
+ {
+ int ret = close(gpo_value_fd[i]);
+ ON_ERROR_RET(ret, "close value file in valve_deinit()", "", -1);
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/source/valve.h b/source/valve.h
new file mode 100644
index 0000000..4e1f710
--- /dev/null
+++ b/source/valve.h
@@ -0,0 +1,35 @@
+#ifndef __VALVE_INIT_H
+#define __VALVE_INIT_H
+#include
+
+typedef enum
+{
+ VALVE_SEN=GPIO_PINDEF_TO_INDEX(GPO1),
+ VALVE_SCLK=GPIO_PINDEF_TO_INDEX(GPO2),
+ VALVE_SDATA_1=GPIO_PINDEF_TO_INDEX(GPO0),
+ VALVE_SDATA_2=GPIO_PINDEF_TO_INDEX(GPO3),
+ VALVE_SDATA_3=GPIO_PINDEF_TO_INDEX(GPO4),
+ VALVE_SDATA_4=GPIO_PINDEF_TO_INDEX(GPO5),
+ VALVE_SDATA_5=GPIO_PINDEF_TO_INDEX(GPO6),
+ VALVE_SDATA_6=GPIO_PINDEF_TO_INDEX(GPO7)
+}valve_pin_enum_t;
+
+typedef struct
+{
+ uint64_t valvedata_1;
+ uint64_t valvedata_2;
+ uint64_t valvedata_3;
+ uint64_t valvedata_4;
+ uint64_t valvedata_5;
+ uint64_t valvedata_6;
+} valvedata_t;
+
+
+#define SCLK_FREQUENCE_KHZ 10000
+
+int valve_init(void);
+int valve_send(uint64_t* valve_data);
+int valve_deinit(void);
+int valve_sendmsg(valvedata_t* valve_data);
+
+#endif
\ No newline at end of file