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