original version
This commit is contained in:
commit
356652e1fe
67
.gitignore
vendored
Normal file
67
.gitignore
vendored
Normal 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
|
||||||
37
README.md
Normal file
37
README.md
Normal file
@ -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代码版本
|
||||||
|
```
|
||||||
|
|
||||||
|
分支编号和分支命名中编号一致
|
||||||
|
|
||||||
|
## 作者
|
||||||
|
|
||||||
|
作者觉得还是不说明作者是谁比较好,免得毕业后有提着示波器的师弟师妹来问问题
|
||||||
BIN
binary/target
Normal file
BIN
binary/target
Normal file
Binary file not shown.
34
protocol/下位机和上位机通信协议.md
Normal file
34
protocol/下位机和上位机通信协议.md
Normal 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
66
source/.gitignore
vendored
Normal 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
24
source/.vscode/c_cpp_properties.json
vendored
Normal 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
76
source/.vscode/settings.json
vendored
Normal 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
86
source/Makefile
Normal 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
141
source/camera_trigger.c
Normal 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
29
source/camera_trigger.h
Normal 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
18
source/delay.s
Normal 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
130
source/encoder.c
Normal 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
35
source/encoder.h
Normal 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
73
source/gpio_common.c
Normal 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
90
source/gpio_common.h
Normal 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
293
source/host_computer.c
Normal 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
45
source/host_computer.h
Normal 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
244
source/main.c
Normal 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
6
source/main.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef __MAIN_H
|
||||||
|
#define __MAIN_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
167
source/queue_reference.c
Normal file
167
source/queue_reference.c
Normal 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:
|
||||||
|
// // 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;
|
||||||
|
}
|
||||||
46
source/queue_reference.h
Normal file
46
source/queue_reference.h
Normal 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
195
source/queue_uint64.c
Normal 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:
|
||||||
|
// // 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;
|
||||||
|
}
|
||||||
48
source/queue_uint64.h
Normal file
48
source/queue_uint64.h
Normal 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
212
source/valve.c
Normal 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
35
source/valve.h
Normal 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
|
||||||
Loading…
Reference in New Issue
Block a user