diff --git a/README.assets/144262e7fd3707720962e083d5304a7e.mp4 b/README.assets/144262e7fd3707720962e083d5304a7e.mp4 new file mode 100644 index 0000000..69a45aa Binary files /dev/null and b/README.assets/144262e7fd3707720962e083d5304a7e.mp4 differ diff --git a/README.assets/20230208200017.jpg b/README.assets/20230208200017.jpg new file mode 100644 index 0000000..23cd009 Binary files /dev/null and b/README.assets/20230208200017.jpg differ diff --git a/README.md b/README.md index 5a5409f..7fd584f 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,23 @@ IO扩展板提供了 - doc为说明文档,包括开发和部署细节、硬件设计的描述等 - [deploy.md](./doc/deploy.md)为开发和部署说明,首先看这个文档 - [hardware_description.md](./doc/hardware_description.md)为PCB设计说明 - - script为配置系统、安装环境、安装可执行文件、卸载可执行文件等的脚本 - protocol为上位机和下位机通信的协议 - hardware下位机主板、接口板、底板等的硬件设计 - source为可执行文件的源程序 +## 喷阀原理 + +喷嘴由两片CNC加工的金属工件拼合而成,每一片工件上有48个深度为喷嘴宽度的一半的沟槽,两片拼合形成截面为矩形的48个喷嘴,通过软管连接到喷阀的气流出口。打开相应喷阀就会在喷嘴处射出气流。喷嘴以喷出气流向下的方向安装,整体平行于传送带滚筒,在传送带末端上方。如图: + +20230208200017 + +工作时射出的气流将瑕疵糖果向下吹走,正常糖果被传送带喷出,如下视频 + + + + + ## 版本 由于经常有不同类型的新要求出现,比如分选糖果、分选烟梗、同为糖果也具有不同的参数,因此不同的下位机型号(注意不是更新,比如同一台机器需要设置新的参数)应建立不同的分支,**主分支无实际意义** diff --git a/doc/deploy.assets/20230208183606.jpg b/doc/deploy.assets/20230208183606.jpg new file mode 100644 index 0000000..4436f18 Binary files /dev/null and b/doc/deploy.assets/20230208183606.jpg differ diff --git a/doc/deploy.assets/5e6af30fa23d9.jpg b/doc/deploy.assets/5e6af30fa23d9.jpg new file mode 100644 index 0000000..75f6f39 Binary files /dev/null and b/doc/deploy.assets/5e6af30fa23d9.jpg differ diff --git a/doc/deploy.md b/doc/deploy.md index e69de29..f2fe235 100644 --- a/doc/deploy.md +++ b/doc/deploy.md @@ -0,0 +1,67 @@ +# 下位机的部署步骤 + +按下面标题的顺序进行,不要轻易跳过步骤,除非你知道你在做什么 + +## 硬件连接 + +### 启动方式选择 + +EPC-9600I-L支持从NAND或者SD卡启动,通过 S7拨码开关控制,拨码开关如图全部设置为OFF,表示从NAND启动。 + +5e6af30fa23d9 + +### IO和供电连接 + +完成以下步骤后,应如下图所示 + +1. 将IO扩展板插入J1牛角座 +2. 按接线图连接相机触发线、编码器的任一相、阀板 +3. 按接线图连接IO扩展板的电源 +4. 按接线图连接EPC9600的电源 +5. 按接线图连接网线到交换机 +6. 连接调试用电脑到交换机 + +20230208183606 + + + +启动交换机、EPC9600、IO扩展板的电源 + +## 软件配置 + +### 登录和配置 + +断开并禁用电脑连接的其他网络和网络适配器(包括无线网),将电脑连接EPC9600的网口设置到192.168.1.0/24网络中 + +SSH登录账号`root`,密码`root`
出厂默认IP为`192.168.1.136` + +可以使用任何SSH软件进行登录,推荐`Xshell`,连接后执行下面的命令设置新的时间和IP: + +```shell +[root@epc-9600 ~]# date 2023.02.08-15:34:00 +[root@epc-9600 ~]# hwclock -w +[root@epc-9600 ~]# eeprom net set ip 192.168.2.9 +``` + +时间不要照抄上面的命令,设置为当前时间即可,IP必须为`192.168.2.9`,执行下面命令重启 + +```shell +[root@epc-9600 ~]# reboot +``` + +### 安装下位机软件 + +因为设置了新的IP,所以将电脑连接EPC9600的网口设置为`192.168.2.11/24`,重新SSH连接IP为`192.168.2.9`的EPC9600。同时启动SFTP软件,推荐`Xftp`,将[script/S90target](../script/S90target)传输到EPC9600的`/tmp`中,运行下面命令配置下位机软件自启动: + +```shell +[root@epc-9600 ~]# wr mv /tmp/S90target /etc/init.d/ +[root@epc-9600 ~]# wr chmod 755 /etc/init.d/S90target +``` + +用相同的放将本仓库release中`app.zip`解压出的下位机软件`target`传输到EPC9600的`/tmp`中,运行下面命令安装下位机软件: + +```shell +[root@epc-9600 ~]# wr mv /tmp/target /root +[root@epc-9600 ~]# wr chmod 755 /root/target +[root@epc-9600 ~]# sync +``` \ No newline at end of file diff --git a/doc/version b/doc/version index ceab6e1..2f45361 100644 --- a/doc/version +++ b/doc/version @@ -1 +1 @@ -0.1 \ No newline at end of file +0.2 \ No newline at end of file diff --git a/script/S90target b/script/S90target new file mode 100644 index 0000000..f4976a9 --- /dev/null +++ b/script/S90target @@ -0,0 +1,2 @@ +#!/bin/sh +/root/target & \ No newline at end of file diff --git a/script/version b/script/version index ceab6e1..9f8e9b6 100644 --- a/script/version +++ b/script/version @@ -1 +1 @@ -0.1 \ No newline at end of file +1.0 \ No newline at end of file diff --git a/source/.vscode/c_cpp_properties.json b/source/.vscode/c_cpp_properties.json index c775f0d..88eb59b 100644 --- a/source/.vscode/c_cpp_properties.json +++ b/source/.vscode/c_cpp_properties.json @@ -4,10 +4,10 @@ "name": "ARM", "includePath": [ "${workspaceFolder}/**", - "/home/miaow/software/arm-2011.03/lib/gcc/arm-none-linux-gnueabi/4.5.2/include/**" + "/home/miaow/software/gcc-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", + "compilerPath": "/home/miaow/software/gcc-arm-2011.03/bin/arm-none-linux-gnueabi-gcc", "cStandard": "gnu99", "cppStandard": "gnu++17", "intelliSenseMode": "linux-gcc-arm", @@ -15,7 +15,7 @@ "browse": { "path": [ "${workspaceFolder}", - "/home/miaow/software/arm-2011.03/lib/gcc/arm-none-linux-gnueabi/4.5.2/include" + "/home/miaow/software/gcc-arm-2011.03/lib/gcc/arm-none-linux-gnueabi/4.5.2/include" ] } } diff --git a/source/.vscode/settings.json b/source/.vscode/settings.json index f16e402..89a674d 100644 --- a/source/.vscode/settings.json +++ b/source/.vscode/settings.json @@ -16,7 +16,9 @@ "queue.h": "c", "stdio.h": "c", "encoder.h": "c", - "semaphore.h": "c" + "semaphore.h": "c", + "socket.h": "c", + "host_computer.h": "c" }, "makefile.launchConfigurations": [ { diff --git a/source/Makefile b/source/Makefile index 31b1c4e..9d4776c 100644 --- a/source/Makefile +++ b/source/Makefile @@ -1,5 +1,5 @@ #makefile for file_ioctl -CROSS_COMPILE ?= /home/miaow/software/arm-2011.03/bin/arm-none-linux-gnueabi- +CROSS_COMPILE ?= /home/miaow/software/gcc-arm-2011.03/bin/arm-none-linux-gnueabi- TARGET := target BUILD_DIR := build diff --git a/source/gpio_common.h b/source/gpio_common.h index 8fcc124..50bf405 100644 --- a/source/gpio_common.h +++ b/source/gpio_common.h @@ -1,3 +1,14 @@ +/** + * @file gpio_common.h + * @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 + */ #ifndef __GPIO_COMMON_H #define __GPIO_COMMON_H diff --git a/source/host_computer.c b/source/host_computer.c index f910e09..5286261 100644 --- a/source/host_computer.c +++ b/source/host_computer.c @@ -1,9 +1,9 @@ /** * @file host_computer.c - * @brief Commnunicate with host computer. Protocal is described in hostcomputer通信协议.md + * @brief Commnunicate with host computer. Protocal is described in 下位机和上位机通信协议.md * @author miaow (3703781@qq.com) - * @version 1.0 - * @date 2022/01/16 + * @version 1.1 + * @date 2023/02/08 * * @copyright Copyright (c) 2022 miaow * @@ -11,6 +11,7 @@ * *
Date Version Author Description *
2022/01/16 1.0 miaow Write this file + *
2023/02/08 1.1 miaow Add fifo empty detection *
*/ #include @@ -84,6 +85,16 @@ static int recvn(int fd, char *buf, int size) return size; } +/** + * @brief Send string "error" + * @param fd The file descriptor that receives error + */ +static void send_error(int fd) +{ + write(fd, "error", 5); + printf("error sent\r\n"); +} + /** * @brief To inspect the status of TCP connection * @param sock_fd The socket @@ -105,7 +116,7 @@ static int is_connected(int sock_fd) void *loop_thread_func(void *param) { printf("loop thread in %s start\r\n", __FILE__); - int need_exit = 0; + int need_exit = 0, error_sent = 0; char pre; uint16_t n_bytes; char type[2]; @@ -143,39 +154,32 @@ void *loop_thread_func(void *param) 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; } @@ -183,19 +187,16 @@ void *loop_thread_func(void *param) 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; } @@ -205,9 +206,9 @@ void *loop_thread_func(void *param) 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) + if (n_bytes - 2 != HOST_COMPUTER_PICTURE_BYTES) { - printf("n_bytes-2!=%d\r\n", 6 * HOST_COMPUTER_PICTURE_ROW_NUM); + printf("n_bytes-2!=%d\r\n", HOST_COMPUTER_PICTURE_BYTES); continue; } int data_index = 0; @@ -220,17 +221,24 @@ void *loop_thread_func(void *param) for (int i = 0; i < HOST_COMPUTER_PICTURE_ROW_NUM; i++) { tmp_one_line_data = 0ul; - for (int j = 0; j < 6; j++) + for (int j = 0; j < HOST_COMPUTER_PICTURE_COLUMN_BYTES; j++) { tmp_one_line_data <<= 8; tmp_one_line_data |= data[data_index++]; } queue_uint64_put(_global_structure.data_q, tmp_one_line_data); } + // printf("queue_num=%d\r\n", _global_structure.data_q->nData); + if (_global_structure.data_q->nData == HOST_COMPUTER_PICTURE_ROW_NUM && !error_sent) + { + error_sent = 1; + send_error(_global_structure.socket_fd); + } } else if (type[0] == 's' && type[1] == 't') { // printf("Start put to cmd queue, param:%d\r\n", (int)atoll(data)); + error_sent = 0; queue_uint64_put(_global_structure.cmd_q, (atoll(data) << 32) | HOSTCOMPUTER_CMD_START); } else if (type[0] == 's' && type[1] == 'p') diff --git a/source/host_computer.h b/source/host_computer.h index 19fbaf5..0e35783 100644 --- a/source/host_computer.h +++ b/source/host_computer.h @@ -1,9 +1,9 @@ /** * @file host_computer.h - * @brief Commnunicate with host computer. Protocal is described in hostcomputer通信协议.md + * @brief Commnunicate with host computer. Protocal is described in 下位机和上位机通信协议.md * @author miaow (3703781@qq.com) - * @version 1.0 - * @date 2022/01/16 + * @version 1.1 + * @date 2023/02/08 * * @copyright Copyright (c) 2022 miaow * @@ -11,6 +11,7 @@ * *
Date Version Author Description *
2022/01/16 1.0 miaow Write this file + *
2023/02/08 1.1 miaow Add 3 macros for picture from host computer *
*/ #ifndef __HOST_COMPUTER_H @@ -23,6 +24,9 @@ #define HOST_COMPUTER_IP "192.168.2.10" #define HOST_COMPUTER_PORT 13452 #define HOST_COMPUTER_PICTURE_ROW_NUM 500 +#define HOST_COMPUTER_PICTURE_COLUMN_NUM 48 +#define HOST_COMPUTER_PICTURE_COLUMN_BYTES (HOST_COMPUTER_PICTURE_COLUMN_NUM / 8) +#define HOST_COMPUTER_PICTURE_BYTES (HOST_COMPUTER_PICTURE_COLUMN_BYTES * HOST_COMPUTER_PICTURE_ROW_NUM) /** * @brief The commonds, ref hostcomputer通信协议.md diff --git a/source/main.c b/source/main.c index e95c89c..5dbea1e 100644 --- a/source/main.c +++ b/source/main.c @@ -1,7 +1,25 @@ +/** + * @file main.c + * @brief Excuting commands, resample data, triggle camera, and send data to valve + * @author miaow (3703781@qq.com) + * @version 1.1 + * @date 2023/02/08 + * + * @copyright Copyright (c) 2022 miaow + * + * @par Changelog: + * + *
Date Version Author Description + *
2022/01/16 1.0 miaow Write this file + *
2023/02/08 1.1 miaow Add debug option for interval of camera triggle + *
+ */ #include #include #include +#include #include +#include #include #include #include @@ -27,6 +45,11 @@ static int camera_trigger_pulse_count = 500; static int valve_should_trigger_pulse_count = 1; static int valve_trigger_pulse_count = 10; static int camera_to_valve_pulse_count = 3015; +#if defined(DEBUG_CAMERA_TRIG_PERIOD) +static struct timeval tv; +static uint64_t camera_period_interval_last_us = 0UL; +static uint64_t camera_period_interval_us = 0UL; +#endif #define ROTATE_UINT64_RIGHT(x, n) ((x) >> (n)) | ((x) << ((64) - (n))) #define ROTATE_UINT64_LEFT(x, n) ((x) << (n)) | ((x) >> ((64) - (n))) @@ -69,19 +92,6 @@ int main(int argc, char *argv[]) hostcomputer_deinit(); queue_uint64_deinit(&data_queue); queue_uint64_deinit(&cmd_queue); - - // encoder_init(on_encoder); - // sleep(100); - // encoder_deinit(); - - // cameratrigger_init(); - // for (int i = 0; i < 100; i++) - // { - // sleep(1); - // cameratrigger_trig(); - // } - // cameratrigger_deinit(); - return 0; } @@ -131,14 +141,21 @@ void process_cmd(uint64_t *cmd) cameratrigger_deinit(); valve_deinit(); queue_uint64_clear(&data_queue); +#if defined(DEBUG_CAMERA_TRIG_PERIOD) + printf("\r\n>>>>>\r\nstatus==SLEEPING\r\ncamera_period_us=%.2f\r\n<<<<<\r\n\r\n", (float)camera_period_interval_us / (float)(count_camera_continues - 1)); + camera_period_interval_us = 0UL; + camera_period_interval_last_us = 0UL; +#else + printf("\r\n>>>>>\r\nstatus==SLEEPING\r\n<<<<<\r\n\r\n"); +#endif count_continues = 0UL; count_valve_continues = 0UL; count_camera_continues = 0UL; - count_valve = 1; count_camera = 0; + count_valve = 1; count_valve_should_be = 2; + status = SLEEPING; - printf("\r\n>>>>>\r\nstatus==SLEEPING\r\n<<<<<\r\n\r\n"); } } } @@ -147,7 +164,7 @@ void valve_test(float ms_for_each_channel) { uint64_t valve_data = 1ul; - for (int i = 0; i < 48; i++) + for (int i = 0; i < HOST_COMPUTER_PICTURE_COLUMN_NUM; i++) { usleep((useconds_t)(ms_for_each_channel * 500.0f)); valvedata.valvedata_1 = valve_data << i; @@ -200,33 +217,34 @@ void on_encoder() { count_continues++; + // send resampled data to valve, the resample cycle is valve_trigger_pulse_count 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); } + // load valve data to valvedata structure 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"); - // } } + // triggle camera in a cycle of camera_trigger_pulse_count if (++count_camera == camera_trigger_pulse_count) { - // printf("camera triggled\r\n"); count_camera = 0; count_camera_continues++; +#if defined(DEBUG_CAMERA_TRIG_PERIOD) + gettimeofday(&tv, NULL); + if (camera_period_interval_last_us != 0UL) + camera_period_interval_us += ((uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec) - camera_period_interval_last_us; + camera_period_interval_last_us = ((uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec); +#endif + cameratrigger_trig(); } } diff --git a/source/main.h b/source/main.h index 7ecc416..7fd4954 100644 --- a/source/main.h +++ b/source/main.h @@ -1,6 +1,23 @@ +/** + * @file main.h + * @brief Excuting commands, resample data, triggle camera, and send data to valve + * @author miaow (3703781@qq.com) + * @version 1.1 + * @date 2023/02/08 + * + * @copyright Copyright (c) 2022 miaow + * + * @par Changelog: + * + *
Date Version Author Description + *
2022/01/16 1.0 miaow Write this file + *
2023/02/08 1.1 miaow Add debug option for interval of camera triggle + *
+ */ #ifndef __MAIN_H #define __MAIN_H +// #define DEBUG_CAMERA_TRIG_PERIOD // uncomment this to print the interval of camera triggle in the unit of usecond #endif diff --git a/source/valve.h b/source/valve.h index 4e1f710..48ff7f0 100644 --- a/source/valve.h +++ b/source/valve.h @@ -1,18 +1,28 @@ +/** + * @file valve.h + * @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 + */ #ifndef __VALVE_INIT_H #define __VALVE_INIT_H #include typedef enum { - VALVE_SEN=GPIO_PINDEF_TO_INDEX(GPO1), - VALVE_SCLK=GPIO_PINDEF_TO_INDEX(GPO2), - VALVE_SDATA_1=GPIO_PINDEF_TO_INDEX(GPO0), - VALVE_SDATA_2=GPIO_PINDEF_TO_INDEX(GPO3), - VALVE_SDATA_3=GPIO_PINDEF_TO_INDEX(GPO4), - VALVE_SDATA_4=GPIO_PINDEF_TO_INDEX(GPO5), - VALVE_SDATA_5=GPIO_PINDEF_TO_INDEX(GPO6), - VALVE_SDATA_6=GPIO_PINDEF_TO_INDEX(GPO7) -}valve_pin_enum_t; + 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 { @@ -24,12 +34,11 @@ typedef struct uint64_t valvedata_6; } valvedata_t; - #define SCLK_FREQUENCE_KHZ 10000 int valve_init(void); -int valve_send(uint64_t* valve_data); +int valve_send(uint64_t *valve_data); int valve_deinit(void); -int valve_sendmsg(valvedata_t* valve_data); +int valve_sendmsg(valvedata_t *valve_data); #endif \ No newline at end of file diff --git a/source/version b/source/version index 9f8e9b6..b123147 100644 --- a/source/version +++ b/source/version @@ -1 +1 @@ -1.0 \ No newline at end of file +1.1 \ No newline at end of file