original version

This commit is contained in:
Miaow 2022-01-16 13:44:50 +08:00
commit 356652e1fe
26 changed files with 2198 additions and 0 deletions

67
.gitignore vendored Normal file
View File

@ -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

1
LISCENSE Normal file
View File

@ -0,0 +1 @@
闭源,私有,保密

37
README.md Normal file
View File

@ -0,0 +1,37 @@
# 下位机
下位机根据传送带脉冲等触发相机,接收上位机给的数据,按其要求控制阀板,用在各种分选机上。
## 目录结构
- binary为编译好的可执行文件
- script为配置系统、安装环境、安装可执行文件、卸载可执行文件等的脚本
- protocol为上位机和下位机通信的协议
- hardware下位机主板、接口板、底板等的硬件设计
- source为可执行文件的源程序
## 版本
由于经常有不同类型的新要求出现,比如分选糖果、分选烟梗、同为糖果也具有不同的参数,因此不同的下位机型号(注意不是更新,比如同一台机器需要设置新的参数)应建立不同的分支,**主分支无实际意义**
分支命名规则(不使用中文,小写无空格)
```shell
b分支编号-p生产环境项目名-t分选对象[-其他特点1[-其他特点2...]]
```
中括号在这里表示可省略的项,中括号本身不应出现在实际命名中,其他特点应字母打头,可有多个,"-"相连
使用Git的tag功能定义版本注意连着tag一起pushGithub仓库的release功能同步发布最新版本
版本号遵循定义如下(不使用中文,小写无空格)
```shell
b分支编号-n编译号-h硬件版本-p协议版本-s脚本版本-r代码版本
```
分支编号和分支命名中编号一致
## 作者
作者觉得还是不说明作者是谁比较好,免得毕业后有提着示波器的师弟师妹来问问题

BIN
binary/target Normal file

Binary file not shown.

View File

@ -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个字节的数据。

66
source/.gitignore vendored Normal file
View File

@ -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

24
source/.vscode/c_cpp_properties.json vendored Normal file
View File

@ -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
}

76
source/.vscode/settings.json vendored Normal file
View File

@ -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:",
"<table>",
"<tr><th>Date <th>Version <th>Author <th>Description",
"<tr><td>{date} <td>1.0 <td>miaow <td>内容",
"</table>",
],
//
"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
}

86
source/Makefile Normal file
View File

@ -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

141
source/camera_trigger.c Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/09 <td>1.0 <td>miaow <td>Write this file
* </table>
*/
#include <camera_trigger.h>
#include <gpio_common.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
#include <time.h>
// 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;
}

29
source/camera_trigger.h Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/09 <td>1.0 <td>miaow <td>Write this file
* </table>
*/
#ifndef __CAMERA_TRIGGER_H
#define __CAMERA_TRIGGER_H
#include <gpio_common.h>
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

18
source/delay.s Normal file
View File

@ -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

130
source/encoder.c Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/09 <td>1.0 <td>Miaow <td>Write this module
* </table>
*/
#include <poll.h>
#include <stdlib.h>
#include <encoder.h>
#include <pthread.h>
#include <stdint.h>
#include <gpio_common.h>
#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;
}

35
source/encoder.h Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/09 <td>1.0 <td>Miaow <td>Write this module
* </table>
*/
#ifndef __ENCODER_H
#define __ENCODER_H
#include <gpio_common.h>
/**
* @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

73
source/gpio_common.c Normal file
View File

@ -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 <gpio_common.h>
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]);
}

90
source/gpio_common.h Normal file
View File

@ -0,0 +1,90 @@
#ifndef __GPIO_COMMON_H
#define __GPIO_COMMON_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#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

293
source/host_computer.c Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/16 <td>1.0 <td>miaow <td>Write this file
* </table>
*/
#include <host_computer.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <pthread.h>
#include <gpio_common.h>
/**
* @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;
}

45
source/host_computer.h Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/16 <td>1.0 <td>miaow <td>Write this file
* </table>
*/
#ifndef __HOST_COMPUTER_H
#define __HOST_COMPUTER_H
#include <queue_uint64.h>
#include <pthread.h>
#include <stdint.h>
#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

244
source/main.c Normal file
View File

@ -0,0 +1,244 @@
#include <valve.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <math.h>
#include <queue_uint64.h>
#include <camera_trigger.h>
#include <encoder.h>
#include <host_computer.h>
/**
* @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();
}
}

6
source/main.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __MAIN_H
#define __MAIN_H
#endif

167
source/queue_reference.c Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/09 <td>1.0 <td>miaow <td>Write this file
* </table>
*/
#include <pthread.h>
#include <queue_reference.h>
#ifdef QUEUE_REFERENCE_DEBUG
#include <stdio.h>
#include <stdint.h>
#endif
#include <stdlib.h>
/**
* @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:
// // 1nData!=0a full queue
// // 2nData为0an 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;
}

46
source/queue_reference.h Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/09 <td>1.0 <td>miaow <td>Write this file
* </table>
*/
#if !defined(__QUEUE_REFERENCE_H)
#define __QUEUE_REFERENCE_H
#include <pthread.h>
/**
* @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

195
source/queue_uint64.c Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/09 <td>1.0 <td>miaow <td>Write this file
* </table>
*/
#include <pthread.h>
#include <queue_uint64.h>
#ifdef QUEUE_UINT64_DEBUG
#include <stdio.h>
#include <stdint.h>
#endif
#include <stdlib.h>
/**
* @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:
// // 1nData!=0a full queue
// // 2nData为0an 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;
}

48
source/queue_uint64.h Normal file
View File

@ -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:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022/01/09 <td>1.0 <td>miaow <td>Write this file
* </table>
*/
#if !defined(__QUEUE_UINT64_H)
#define __QUEUE_UINT64_H
#include <pthread.h>
#include <stdint.h>
/**
* @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

212
source/valve.c Normal file
View File

@ -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 <valve.h>
#include <gpio_common.h>
#include <pthread.h>
#include <unistd.h>
// 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;
}

35
source/valve.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __VALVE_INIT_H
#define __VALVE_INIT_H
#include <gpio_common.h>
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