diff --git a/doc/develop_and_deploy.md b/doc/develop_and_deploy.md index 785e93a..fbf7b8e 100644 --- a/doc/develop_and_deploy.md +++ b/doc/develop_and_deploy.md @@ -2,7 +2,7 @@ ## 开发 -本次开发基于zynq芯片,因此FPGA设计软件为Vitis中包含的[Vivado 2021.2](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/vitis.html),Linux编译工具为[petalinux 2022.1](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/embedded-design-tools.html),Linux应用程序编译工具为linaro的[arm-linux-gnueabihf-gcc 12.0.1](https://snapshots.linaro.org/gnu-toolchain/12.0-2022.02-1/arm-linux-gnueabihf/)。 +本次开发基于zynq xc7z010-1clg400芯片,因此FPGA设计软件为Vitis中包含的[Vivado 2021.2](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/vitis.html),Linux编译工具为[petalinux 2022.1](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/embedded-design-tools.html),Linux应用程序编译工具为linaro的[arm-linux-gnueabihf-gcc 12.0.1](https://snapshots.linaro.org/gnu-toolchain/12.0-2022.02-1/arm-linux-gnueabihf/)。 ### 生成硬件描述文件 @@ -19,19 +19,17 @@ $ petalinux-create -t modules --name encoder --enable ``` -2. 上传驱动代码[source/linux_driver/fifo.c](../source/linux_driver/fifo.c)和[source/linux_driver/encoder.c](../source/linux_driver/encoder.c) +2. 分别上传驱动代码[source/linux_driver/fifo.c](../source/linux_driver/fifo.c)和[source/linux_driver/encoder.c](../source/linux_driver/encoder.c)到下面的目录中 ```shell - $ cd ~/ps-linux/project-spec/meta-user/recipes-modules/fifo - $ rz # 上传source/linux_driver/fifo.c - $ cd ~/ps-linux/project-spec/meta-user/recipes-modules/encoder - $ rz # 上传source/linux_driver/encoder.c + ~/ps-linux/project-spec/meta-user/recipes-modules/fifo/files + ~/ps-linux/project-spec/meta-user/recipes-modules/encoder/files ``` - -3. 上传xsa文件并config + +3. 上传硬件描述文件[source/petalinux_hwdescription/system_wrapper.xsa](source/petalinux_hwdescription/system_wrapper.xsa)到`ps-linux`目录中并config ```shell - $ cd ~/ps-linux; rz # 上传source/petalinux_hwdescription/system_wrapper.xsa + system_wrapper.xsa上传到~/ps-linux $ petalinux-config --get-hw-description system_wrapper.xsa ``` @@ -71,7 +69,7 @@ ```shell $ cd ~/ps-linux/project-spec/meta-user/recipes-bsp/device-tree/files $ rm system-user.dtsi - $ rz # 上传source/petalinux_devicetree/system-user.dtsi + 上传source/petalinux_devicetree/system-user.dtsi ``` 5. 配置kernel,使用命令`petalinux-config -c kernel`,按下面提示或[source/petalinux_config/kernel.cfg](../source/petalinux_config/kernel.cfg)配置 @@ -188,38 +186,52 @@ # ├─imagefeature-hwcodecs (勾选为星号) # ├─imagefeature-package-management (勾选为星号) # modules - # ├─encoder (勾选为星号) - # └─fifo (勾选为星号) + # ├─encoder (不选) + # ├─fifo (不选) # PetaLinux RootFS Settings # ├─ADD_EXTRA_USERS (root:3703;petalinux:3703;) # ├─ADD_USERS_TO_GROUPS (petalinux:audio,video;) # └─ADD_USERS_TO_SUDOERS (petalinux) ``` -### 编译PETALINUX工程 +### 编译系统 1. 编译工程,使用命令`petalinux-build`。编译完成,在当前工程目录下生成images文件夹,该命令将生成设备树文件、FSBL文件、U-Boot文件,Linux Kernel文件和rootfs文件镜像 2. 制作BOOT.BIN启动文件,具体命令如下: ```shell - $ cd ~/petalinux-projects/ps-linux/images/linux/ # 生成的BOOT.BIN也在该路径下 + $ cd ~/ps-linux/images/linux/ # 生成的BOOT.BIN在该路径下 $ petalinux-package --boot --fsbl ./zynq_fsbl.elf --fpga ./system.bit --u-boot ./u-boot.elf --force ``` + + +### 编译驱动 + +依次运行如下命令,单独编译3个驱动程序 + +```shell +$ petalinux-build -c fifo +$ petalinux-build -c encoder +``` + +编译后的模块文件为` ps-linux/build/tmp/sysroots-components/zynq_generic/fifo/lib/modules/5.15.19-xilinx-v2022.1/extra/fifo.ko`和`ps-linux/build/tmp/sysroots-components/zynq_generic/encoder/lib/modules/5.15.19-xilinx-v2022.1/extra/encoder.ko` + +### 编译应用程序 + +在运行make时要设置好交叉编译工具链前缀,命令如下 +```shell +$ make CROSS_COMPILE=交叉编译工具链前缀 +例如 make CROSS_COMPILE=/home/miaow/software/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf- +``` + +编译后的可执行文件为工程目录的`build/target`,交叉编译工具链前缀也可以在Makefile中修改设定 ## 部署 -> 注意:这部分所需的文件按上一章节编译得到或者从github的release中下载 +有两种方式部署,一种是修改文件系统,这也是我第一次构建这个系统时的操作;另一种是直接写入镜像,推荐使用这种方式,省时省力不易出错 -### SSH连接 - -1. 电脑网卡设置到开发板同一网段 - -2. SSH连接信息如下 - - ```shell - $ sshpass -p "3703" ssh root@192.168.10.10 -p 22 - ``` +> 注意:修改文件系统方法所需的文件按上一章节编译得到或者从github的release中下载;直接写入镜像所需的文件在release中 ### 修改文件系统 @@ -267,31 +279,36 @@ $ reboot ``` -7. 安装编译得到的驱动文件fifo.ko和encode.ko,并设置自动加载,对应脚本见[script/loadfifo.sh](../script/loadfifo.sh)和[script/loadencoder.sh](../script/loadencoder.sh) + - ssh方式,root登录: +7. 电脑网卡设置到开发板同一网段 SSH连接信息如下 ```shell - $ cd ~; rz #上传fifo.ko - $ rz # 上传encoder.ko - $ mv fifo.ko encoder.ko /lib/modules/[内核版本]/kernel/drivers/ + 在电脑上执行下面命令 + $ sshpass -p "3703" ssh root@192.168.10.10 -p 22 + ``` + +8. 安装编译得到的驱动文件fifo.ko和encode.ko,并设置自动加载,对应自启脚本可以如下方式写入,也可以直接上传[script/loadfifo.sh](../script/loadfifo.sh)和[script/loadencoder.sh](../script/loadencoder.sh),ssh方式,root登录: + + ```shell + 上传fifo.ko、encoder.ko到/lib/modules/[内核版本]/kernel/drivers/ $ cd /lib/modules/[内核版本]; depmod $ set +H $ echo -e "#!/bin/sh\nmodprobe fifo" > /etc/init.d/loadfifo.sh $ echo -e "#!/bin/sh\nmodprobe encoder" > /etc/init.d/loadencoder.sh - $ chmod 755 /etc/init.d/loadfifo.sh - $ chmod 755 /etc/init.d/loadencoder.sh + $ chmod 755 /etc/init.d/loadfifo.sh /etc/init.d/loadencoder.sh $ cd /etc/rc5.d $ ln -s ../init.d/loadfifo.sh S20loadfifo.sh $ ln -s ../init.d/loadencoder.sh S20loadencoder.sh ``` -8. 安装编译得到的应用程序target,并设置自启动,对应脚本见[script/target.sh](../script/target.sh) +9. 安装编译得到的应用程序target,并设置自启动,对应脚本见[script/target.sh](../script/target.sh) ssh方式,root登录: ```shell - $ cd ~; rz # 上传target + 上传target到/home/root + $ cd ~ $ chmod 755 target $ set +H $ echo -e "#!/bin/sh\nif [ -x /home/root/target ]; then\n /home/root/target\nfi" > /etc/init.d/target.sh @@ -299,35 +316,36 @@ $ cd /etc/rc5.d $ ln -s ../init.d/target.sh S99target.sh ``` - -9. \[可选\] 设置.bashrc,美化PS1,对应脚本见[script/.profile](../script/.profile)和[script/.bashrc](../script/.bashrc) + +10. \[可选\] 设置.bashrc,修改PS1,对应脚本见[script/.profile](../script/.profile)和[script/.bashrc](../script/.bashrc) ```shell - $ cd ~; rz # 上传.bashrc - $ rz # 上传.profile + $ cd ~; rm .bashrc .profile + 上传.bashrc和.profile到/home/root $ if [ ! -a /home/petalinux/.profile ]; then cp /home/root/.profile /home/petalinux/ fi $ if [ ! -a /home/petalinux/.bashrc ]; then cp /home/root/.bashrc /home/petalinux/ & chown petalinux:petalinux -R /home/petalinux fi $ source ~/.profile ``` - -10. \[可选\] 安装ncurses-6.3和htop. + +11. \[可选\] 安装ncurses-6.3和htop ```shell $ cd ~; rz # 上传ncurses-6.3.tar.gz $ tar xmzf /home/root/ncurses-6.3.tar.gz -C /usr/ $ rz # 上传htop.tar.gz $ tar xmzf /home/root/htop.tar.gz -C /usr/ + $ echo "export TERMINFO=/usr/share/terminfo" >> /etc/profile + $ reboot ``` -### SD卡启动 +### 直接写入镜像 -1. 给SD卡创建DOS分区表,然后分2个区并创建文件系统,细节如下表: +强烈推荐的傻瓜式的方法,在windows上准备好正版[DiskGenius标准版或专业版](https://www.diskgenius.cn/),盗版有BUG,从release中下载sdimage.pmfx文件 - | 扇区 | 大小 | 分区类型 | 文件系统 | 卷标 | - | -------------- | -------------- | ----------------- | -------- | ------ | - | 2048~x扇区 | 100M | C W95 FAT32 (LBA) | FAT32 | boot | - | x扇区~最后扇区 | ≈SD卡大小-100M | 83 Linux | ext4 | rootfs | +1. 在windows上插入16G的TF卡 +2. 打开DiskGenius +3. 左侧栏选中TF卡,右键,从镜像文件还原磁盘 +4. 选sdimage.pmfx文件 +5. 点击开始 -2. 将Github Release中的BOOT.BIN、boot.scr和image.ub复制到boot分区;将rootfs.tar.gz解压到rootfs分区。 - -3. 拨码开关拨到SD卡启动,插入SD卡到XME0724底板上,上电启动。 +把TF卡插回板子,启动方式拨到SD卡启动,上电。要进入系统,参考修改文件系统章节的第7步。 diff --git a/doc/hardware_description.assets/2.png b/doc/hardware_description.assets/system_arch.png similarity index 100% rename from doc/hardware_description.assets/2.png rename to doc/hardware_description.assets/system_arch.png diff --git a/doc/hardware_description.md b/doc/hardware_description.md index 194354f..27f7a20 100644 --- a/doc/hardware_description.md +++ b/doc/hardware_description.md @@ -1,54 +1,77 @@ # 硬件平台 -PL端主要由4个外设组成,分别时**风扇控制器**(FAN),**编码和分频控制器**(ENCODER),**先入先出队列**(FIFO)和**阀板控制器**(VALVE)。其中阀板控制器没有提供AXI接口,因此并没有映射寄存器,软件也无法进行控制。各个控制器的连接关系如下图所示。 +PL端主要由4个外设组成,分别时**风扇控制器**(FAN),**编码和分频控制器**(ENCODER),**先入先出队列*(FIFO),阀板控制器**(VALVE)。其中阀板控制器没有提供AXI接口,因此并没有映射寄存器,软件也无法进行控制。各个控制器的连接关系如下图所示。 -![2](hardware_description.assets/2.png) +![2](hardware_description.assets/system_arch.png) -由于开发板的PL端没有自带晶振,所以4个外设由统一的同步时钟驱动,时钟源来自PS端,为200MHz,软件不可修改。外部编码器信号输入**编码和分频控制器**,控制器根据软件设置的阀触发分频值和相机触发分频值对编码器信号进行分频,分频后的信号用于驱动喷阀动作和触发相机拍照。为同步触发相机和移出队列以及保持队列中数据的动态平衡,**先入先出队列**在相机触发同时输出一个数据,即**先入先出队列**读信号和相机触发共用同一个信号。而由于电磁阀的物理特性导致电磁阀无法以触发相机的频率进行开关,因此**阀板控制器**对先入先出队列输出总线上的数据进行重采样,即按照**编码和分频控制器**输出的阀触发信号更新并转换为阀板协议,输出电磁阀的状态。设计的风扇控制器用于驱动风扇的启停,给ZYNQ芯片进行降温,防止芯片过热导致工作中出现问题。 +由于开发板的PL端没有自带晶振,所以4个外设由统一的同步时钟驱动,时钟源来自PS端,为200MHz,软件不可修改。外部编码器信号输入**编码和分频控制器**,控制器根据软件设置的阀触发分频值和相机触发分频值对编码器信号进行分频,分频后的信号用于驱动喷阀动作和触发相机拍照。光谱相机的触发频率较慢,为编码器频率/CDIV;彩色相机触发频率快,为⌊编码器频率/CDIV⌋\* 4。上位机发来的喷阀动作数据有两个,对应的分别是光谱和彩色相机的识别结果,都是为256宽度*1024高度。识别结果存储到**先入先出队列**中。 -## 生成硬件描述文件 +为同步触发相机和队列移出信号以及保持队列中数据的动态平衡,两个先入先出队列在彩色相机触发同时输出一个数据,即**先入先出队列**的读信号和彩色相机触发共用同一个信号。而由于电磁阀的物理特性导致电磁阀可能无法以触发相机的频率进行开关,因此**阀板控制器**对先入先出队列输出总线上的数据重采样,即按照**编码和分频控制器**输出的阀触发信号读入。两个先入先出队列移出的数据在**阀板控制器**中进行按位或运算,得到的数据转换为阀板协议,输出给阀板。**风扇控制器**用于驱动风扇的启停,给ZYNQ芯片进行降温,防止芯片过热导致工作中出现问题。由于风扇寿命短,因此目前采用散热片方案,风扇不开。 -1. 创建名为test_lower_machine的工程,打开**Block Design**,添加ZYNQ7 Processing System、ip_fifo、ip_encoder、ip_fan、valve_interfaces模块。 -2. 在ZYNQ7 Processing System中勾选Quad SPI Flash [1-6]、Ethernet 0 [16-27]、USB 0 [28-39]、SD 0 [40-45]、SD 1 [46-51]、UART 0 [14-15]、TTC 0 [EMIO]、GPIO MIO {Ethernet PHY Reset [7]、USB PHY Reset[8]} -3. 按顺序点击**Generate Outputs Product** -> **Create HDL Wrapper** -> **Generate Bitstream**,**File** -> **Export Export Hardware** ,得到xsa文件。 +## PS模块 +下面列出需要开启的外设和采用的IO口,BANK0为LVCMOS3.3V,BANK1为LVCOMS1.8V +| 外设 | IO | 备注 | +| --------------------------------------------------- | ---------------------- | -------------------------------------- | +| Quad SPI Flash | 1~6 | Single SS 4bit IO,Fast , ss_b pullup | +| Ethernet 0
MDIO | 16~27
52~53 | Fast, all pullup | +| USB0 | 28~39 | Fast, all pullup | +| SD0 | 40~45 | Fast, all pullup | +| SD1 | 46~51 | Fast, all pullup | +| UART0 | 14~15 | all pullup, baud 115200, 8bits, 1 stop | +| TTC0 | EMIO | | +| GPIO MIO
Ethernet PHY Reset
USB PHY Reset | 所有剩下的
7
8 | Fast, all pullup | + +时钟配置上,ARM为666.66MHz,DDR为533.33MHz,用IOPLL给PL端提供200MHz的FCLK_CLK0。 + +如果用的是7010的板子,内存选忽略下面这一段:内存配置上,选择DDR3,数据位宽32bit=16bit/chip \* 2chip, 4096Mbits/chip,频率533.33,速度等级为1066F,行/列/Bank地址宽度为15/10/3,CL=7,CWL=6,RCD=7,RP=7,RC=51ns,FAW=40ns + +ARM核上还开启AXI GP0接口与PL通信 ## ENCODER模块 -1. encoder模块自定义接口in_signal、out_signal_camera_posedge、out_signal_valve_posedge、out_signal_camera、out_signal_valve +encoder模块主要接口为in_signal、out_signal_camera_posedge、out_signal_camera_posedge_x4、out_signal_valve_posedge、out_signal_camera、out_signal_camera_x4、out_signal_valve -2. in_signal接口与外部编码器相连,接收外部编码器信号 +接口比较乱,这个模块内部逻辑也实现的不优雅,这得怪老倪动不动就改需求 -3. out_signal_camera为分频后的信号,用于驱动相机拍照 +1. in_signal接口与外部编码器相连,接收外部编码器信号 -4. out_signal_camera_posedge为out_signal_camera的上升沿,该信号输出给fifo模块的rd_en接口,用来驱动fifo模块将数据加载到AXI总线上 +2. out_signal_camera用于触发光谱相机拍照,可以认为光谱相机一次触发产生4行喷阀数据 -5. out_signal_valve_posedge为out_signal_valve的上升沿,该信号输出给valve_interfaces模块的valve_en信号,用于输出AXI总线上的数据,驱动喷阀动作 +3. out_signal_camera_x4则为out_signal_camera的4倍频率,触发彩色相机,彩色相机一次产生1行喷阀数据 -6. ENCODER模块寄存器说明见[doc/pl_reference_mannual.md](pl_reference_mannual.md)中的ENCODER控制器部分 +4. out_signal_camera_posedge_x4为out_signal_camera_x4的上升沿,控制FIFO的读出 - +5. out_signal_valve_posedge为out_signal_valve的上升沿,驱动**阀板控制器**动作 + + +ENCODER模块输入输出频率的详细计算方式和寄存器说明见[doc/pl_reference_mannual.md](pl_reference_mannual.md)中的ENCODER控制器部分 ## FIFO模块 -1. fifo模块自定义接口rd_en、dout[383:0]、empty、full、almost_full、almost_empty、data_count[11:0]、fifo_valid -2. rd_en接收out_signal_camera_posedge传来的信号,用来驱动fifo模块将数据加载到AXI总线上 -3. dout[383:0]为驱动喷阀动作的总数据,数据位宽为384bit -4. 当empty信号拉高时,表示fifo中数据已经为空,无法输出有效数据 -5. fifo模块寄存器说明见[doc/pl_reference_mannual.md](pl_reference_mannual.md)中的FIFO控制器部分 +FIFO模块的主要接口为rd_en、dout[383:0]、empty、full、almost_full、almost_empty、data_count[11:0]、fifo_valid + +1. rd_en接收ENCODER模块传来的信号,控制模块内部FIFO按ENCODER模块所需频率进行读取和输出 +2. dout[383:0]为FIFO中读出数据,数据位宽为384bit +3. empty信号为1表示fifo中数据已经为空,无法输出有效数据,但输出寄存器仍然保持上次输出的值 + +FIFO模块寄存器说明见[doc/pl_reference_mannual.md](pl_reference_mannual.md)中的FIFO控制器部分 ## FAN模块 -1. fan模块自定义接口fan用于控制风扇的启停 -2. fan模块寄存器说明见[doc/pl_reference_mannual.md](pl_reference_mannual.md)中的FAN控制器部分 +fan模块接口fan输出为PWM波形,连接到外部的风扇驱动电路。 -> Note:fan模块代码中设计了PWM调速功能,但由于硬件兼容问题导致,无法观察到调速现象。但正常的启停可以做到 +fan模块寄存器说明见[doc/pl_reference_mannual.md](pl_reference_mannual.md)中的FAN控制器部分 + +> Note:fan模块代码中设计了PWM调速功能,对于带程序的交流风扇,无法观察到调速现象。但正常的启停可以做到 ## VALVE_INTERFACES模块 -1. valve_interfaces模块自定义接口total_valve_data[383:0]、empty、valve_en、sclk[7:0]、sen[7:0]、sdata[7:0] -2. total_valve_data[383:0]接收fifo模块dout接口传输的数据,对其重采样后由sdata接口输出给各个阀板 -3. empty信号与fifo模块的empty信号相连。valve_interfaces模块检测到empty信号为高后,表示fifo中的数据被读空,此时将total_valve_data的384bit的数据全部置为0,然后输出给阀板 -4. valve_en信号拉高后将384bit的数据输出给阀板,更新喷阀状态。该信号不拉高时则不更新喷阀状态 -5. sclk[7:0]、sen[7:0]、sdata[7:0]为对应8块阀板的时钟信号线、使能信号线以及数据信号线 +VALVE_INTERFACES模块的主要接口为total_valve_data[383:0]、empty、valve_en、sclk[7:0]、sen[7:0]、sdata[7:0]。VALVE_INTERFACES会将total_valve_data_a和total_valve_data_b两路数据进行或运算,得到同为384位的单路数据。 + +1. total_valve_data接收FIFO模块中读出的数据。 + +2. empty信号与两个FIFO模块的empty信号相连。检测到任意一个empty信号为高时,无论输入数据如何,只按协议输出全关给阀板。 +3. valve_en信号上升沿将total_valve_data按协议发送给阀板 +4. sclk[7:0]、sen[7:0]、sdata[7:0]连接到外部差分芯片,为最多8块阀板的时钟信号线、使能信号线以及数据信号线 diff --git a/doc/pl_reference_mannual.assets/fifo_ecr.png b/doc/pl_reference_mannual.assets/fifo_ecr.png new file mode 100644 index 0000000..dfbf006 Binary files /dev/null and b/doc/pl_reference_mannual.assets/fifo_ecr.png differ diff --git a/doc/pl_reference_mannual.assets/fifo_regs.png b/doc/pl_reference_mannual.assets/fifo_regs.png index 3264aa0..2e0e6c8 100644 Binary files a/doc/pl_reference_mannual.assets/fifo_regs.png and b/doc/pl_reference_mannual.assets/fifo_regs.png differ diff --git a/doc/pl_reference_mannual.assets/regs.xlsx b/doc/pl_reference_mannual.assets/regs.xlsx index fd65e48..b5bbc85 100644 Binary files a/doc/pl_reference_mannual.assets/regs.xlsx and b/doc/pl_reference_mannual.assets/regs.xlsx differ diff --git a/doc/pl_reference_mannual.md b/doc/pl_reference_mannual.md index 2950326..caf3b46 100644 --- a/doc/pl_reference_mannual.md +++ b/doc/pl_reference_mannual.md @@ -105,9 +105,9 @@ ENCODER模块的寄存器主要有控制寄存器 (ENCODER_CR)、阀触发分频 | **Field** | **Description** | | :---------- | :----------------------------------------------------------- | | 位31:3 保留 | 必须保持复位值 | -| 位2 **VTS** | 内部触发信号 (Virtual Triggle Signal)
**MOD**位置1时,由上位机软件写入,将该位信号作为触发信号 | -| 位1 **MOD** | 模式选择 (Mode)
0: 外部触发模式,外部触发编码器转动
1: 内部触发模式,上位机软件模拟触发信号 | -| 位0 **CLR** | 清除缓存 (Clear)
0: 正常工作 1: 清除编码和分频控制器内部的分频计数值,不影响VDIV和CDIV
注意: 程序置位该位后后需再写入0,使计数器退出清零状态,正常工作 | +| 位2 **VTS** | 内部触发信号 (Virtual Triggle Signal)
**MOD**位置1时,由软件写入,将该位信号直接充当触发信号
0: 低电平
1: 高电平 | +| 位1 **MOD** | 模式选择 (Mode)
0: 外部触发模式,外部触发编码器转动
1: 内部触发模式,软件模拟触发信号 | +| 位0 **CLR** | 清除缓存 (Clear)
0: 正常工作
1: 清除编码和分频控制器内部的分频计数值,不影响VDIV和CDIV
注意: 程序置位该位后后需再写入0,使计数器退出清零状态,正常工作 | #### ENCODER阀触发分频寄存器 (ENCODER_VDIVR) @@ -117,7 +117,7 @@ ENCODER模块的寄存器主要有控制寄存器 (ENCODER_CR)、阀触发分频 | **Field** | **Description** | | :-------------- | :----------------------------------------------------------- | -| 位31:0 **VDIV** | 阀触发分频值
写入数据后编码和分频控制器自动清除缓存并应用新的数值
注意:0表示不间断触发,即PL端每个时钟周期均触发阀模块 | +| 位31:0 **VDIV** | 阀触发分频值
阀控制器重采样频率和编码器脉冲的分频值, 写入数据后编码和分频控制器自动清除缓存并应用新的数值
注意:0表示不间断触发,即PL端每个时钟周期均触发阀模块 | #### ENCODER相机触发分频寄存器 (ENCODER_CDIVR) @@ -194,6 +194,18 @@ FIFO的写宽度为384bit,因此需12个32位寄存器**FIFO_DAT0**-**FIFO_DAT | 位12 **FU** | 队列满标志 (Almost Full)
0: 队列中的有效数据小于FIFO数据深度
1: 队列中的有效数据达到FIFO数据深度 | | 位11:0 **CNT** | 队列数据数量 (Data Count)
该值指示队列中的数据数量
注意:一个数据为384位宽 | +#### FIFO空计数寄存器 (FIFO_ECR) + +队列为空后被读的次数 + +偏移地址: 0x34
复位值: 0x0000 0000 + +![image-20220613212701816](pl_reference_mannual.assets/fifo_ecr.png) + +| **Field** | **Description** | +| :------------- | :----------------------------------------------------------- | +| 位31:0 **CNT** | 队列为空读次数 (Empty Count)
该值指示队列为空后被读的次数
注意:改寄存器只读,仅在FIFO复位时自动清零 | + #### FIFO控制寄存器 (FIFO_CR) 偏移地址: 0x38
复位值: 0x0000 00xx @@ -210,4 +222,4 @@ FIFO的写宽度为384bit,因此需12个32位寄存器**FIFO_DAT0**-**FIFO_DAT FIFO寄存器可映射为32位可寻址寄存器,如下表所述: -image-20220613220214064 + diff --git a/doc/version b/doc/version index 9f8e9b6..ea710ab 100644 --- a/doc/version +++ b/doc/version @@ -1 +1 @@ -1.0 \ No newline at end of file +1.2 \ No newline at end of file diff --git a/hardware/pl_platform/version b/hardware/pl_platform/version index 9f8e9b6..b123147 100644 --- a/hardware/pl_platform/version +++ b/hardware/pl_platform/version @@ -1 +1 @@ -1.0 \ No newline at end of file +1.1 \ No newline at end of file diff --git a/protocol/version b/protocol/version index ea710ab..a58941b 100644 --- a/protocol/version +++ b/protocol/version @@ -1 +1 @@ -1.2 \ No newline at end of file +1.3 \ No newline at end of file diff --git a/protocol/下位机和上位机通信协议.md b/protocol/下位机和上位机通信协议.md index e9c7b0a..fe28de7 100644 --- a/protocol/下位机和上位机通信协议.md +++ b/protocol/下位机和上位机通信协议.md @@ -1,4 +1,4 @@ -# 下位机和上位机通信协议 V1.2 +# 下位机和上位机通信协议 V1.3 | 起始 | 长度高 | 长度低 | 类型高 | 类型低 | 数据字节1 | ... | 数据字节n | 校验低 | 校验高 | 结束 | | ---- | ------ | ------ | ------ | ------ | --------- | ---- | --------- | ------ | ------ | ---- | @@ -18,13 +18,11 @@ - 设置相机触发周期对应的脉冲数sc,**长度**10,数据为十进制字符串,'0''0''0''0''0''0''5''0'表示值50,'5'在前,'0'在后 - 设置阀板动作对应的脉冲数sv,**长度**10,数据为十进制字符串,'0''0''0''0''0''0''5''0'表示值50,'5'在前,'0'在后 - 设置相机触发到阀板动作的延迟脉冲数sa,**长度**10,数据为十进制字符串,'0''0''0''0''0''0''5''0'表示值50,'5'在前,'0'在后 - - 设置相机触发到阀板动作的延迟脉冲数sb,**长度**10,数据为十进制字符串,'0''0''0''0''0''0''5''0'表示值50,'5'在前,'0'在后 - 数据命令da, **长度**为视需求而定,数据要有(**长度**-2)个字节 - - 数据命令db, **长度**为视需求而定,数据要有(**长度**-2)个字节 - 数据 - 数据就是阀数据,其实这是一个命令,也就是数据命令'da''db',分到数据这一节写是因为它的参数格式和其他命令不同,下表为字节排序,接收时从右往左,也就是数据字节1先接收到,然后是数据字节2,最后是数据字节(m-1)。 + 数据就是阀数据,其实这是一个命令,也就是数据命令'da',分到数据这一节写是因为它的参数格式和其他命令不同,下表为字节排序,接收时从右往左,也就是数据字节1先接收到,然后是数据字节2,最后是数据字节(m-1)。 阀1代表面向各块阀板,最靠近右边的阀,所以最左边的为阀n diff --git a/source/linux_app/Makefile b/source/linux_app/Makefile index 71c2af5..ec46912 100644 --- a/source/linux_app/Makefile +++ b/source/linux_app/Makefile @@ -1,4 +1,3 @@ -#makefile for file_ioctl CROSS_COMPILE ?= /home/miaow/software/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf- TARGET := target BUILD_DIR := build diff --git a/source/linux_app/data_filter.c b/source/linux_app/data_filter.c new file mode 100644 index 0000000..fff8bb2 --- /dev/null +++ b/source/linux_app/data_filter.c @@ -0,0 +1,52 @@ +/** + * @file data_filter.c + * @brief Manage the hardware encoder unit + * @author miaow (3703781@qq.com) + * @version 1.0 + * @date 2022/08/06 + * + * @copyright Copyright (c) 2022 miaow + * + * @par Changelog: + * + *
Date Version Author Description + *
2022/08/06 1.0 Miaow Write this module + *
+ */ + +#include +#include +#include + +void datafilter_init(datafilter_typedef *filter, int w_size) +{ + filter->cache = (int *)malloc(sizeof(int) * (w_size + 1)); + memset(filter->cache, 0, sizeof(int) * (w_size + 1)); + + filter->w_size = w_size; + filter->sum = 0; + filter->head = 0; +} + +int datafilter_calculate(datafilter_typedef *filter, int z) +{ + filter->cache[filter->head] = z; + filter->head = (filter->head + 1) % (filter->w_size + 1); + filter->sum = filter->sum + z - filter->cache[filter->head]; + if (filter->w_size != 0) + return filter->sum / filter->w_size; + else + return -1; +} + +void datafilter_deinit(datafilter_typedef *filter) +{ + if (filter->cache != NULL) + { + free(filter->cache); + filter->cache = NULL; + } + filter->sum = 0; + filter->w_size = 0; + filter->head = 0; +} diff --git a/source/linux_app/data_filter.h b/source/linux_app/data_filter.h new file mode 100644 index 0000000..b2e8c8c --- /dev/null +++ b/source/linux_app/data_filter.h @@ -0,0 +1,34 @@ +/** + * @file data_filter.h + * @brief Manage the hardware encoder unit + * @author miaow (3703781@qq.com) + * @version 1.0 + * @date 2022/08/06 + * + * @copyright Copyright (c) 2022 miaow + * + * @par Changelog: + * + *
Date Version Author Description + *
2022/08/06 1.0 Miaow Write this module + *
+ */ +#ifndef __DATA_FILTER_H +#define __DATA_FILTER_H + +typedef struct +{ + int w_size; + int head; + int sum; + int *cache; +} datafilter_typedef; + +void datafilter_init(datafilter_typedef *filter, int w_size); + +int datafilter_calculate(datafilter_typedef *filter, int z); + +void datafilter_deinit(datafilter_typedef *filter); + + +#endif diff --git a/source/linux_app/fifo_dev.c b/source/linux_app/fifo_dev.c index 9bb04f4..ebcc0be 100644 --- a/source/linux_app/fifo_dev.c +++ b/source/linux_app/fifo_dev.c @@ -1,12 +1,21 @@ /** * @file fifo_dev.c * @brief Operate the hardware fifo with Linux application - * @details Call fifo_dev_init() paired with fifo_dev_deinit() as their names imply, fifo_dev_write() can be executed several times to operate the hardware fifo between fifo_dev_init() and fifo_dev_deinit() + * @details Call fifo_dev_init() paired with fifo_dev_deinit() as their names imply, fifo_dev_write_xxx() can be executed several times to operate the hardware fifo between fifo_dev_init() and fifo_dev_deinit() * @mainpage github.com/NanjingForestryUniversity - * @author miaow - * @email 3703781@qq.com - * @version 1.0 - * @date 2022/06/09 + * @author miaow (3703781@qq.com) + * @version 1.1 + * @date 2022/08/07 + * @mainpage github.com/NanjingForestryUniversity + * + * @copyright Copyright (c) 2022 miaow + * + * @par Changelog: + * + *
Date Version Author Description + *
2022/06/09 1.0 miaow Write this file + *
2022/08/07 1.1 miaow Add ignore_row_num to fifo_dev_write_frame + *
*/ #include @@ -14,13 +23,15 @@ #include #include #include +#include #include #include #include +#include #define FIFO_CMD_FUNCTION_CLEAR 1 #define FIFO_CMD_FUNCTION_PADDING 2 - +#define FIFO_CMD_GET_EMPTYCOUNT 3 static int fifo_dev_fd = -1; static char perror_buffer[128]; @@ -37,15 +48,34 @@ int fifo_dev_init() return 0; } +/** + * @brief Get the count of read operation when the fifo is empty. + * @note The empty read count will be set to zero at fifo_dev_clear() only. + * @return >=0 - success, other - error + */ +int fifo_dev_get_emptycount() +{ + int count; + int res = ioctl(fifo_dev_fd, _IOR('D', FIFO_CMD_GET_EMPTYCOUNT, 0), &count); + + ON_ERROR_RET(res, "", "", -1); + return count; +} + /** * @brief Set value to put of a frame. - * @param valve_data An array 32bytes * 600rows. + * @param valve_data An array HOST_COMPUTER_PICTURE_COLUMN_BYTES bytes * HOST_COMPUTER_PICTURE_ROW_NUM rows. + * @param ignore_row_num Remove the ignore_row_num rows at the beginning of the frame. range: [0, HOST_COMPUTER_PICTURE_BYTES) * @return 0 - success, other - error */ -int fifo_dev_write_frame(void *valve_data) +int fifo_dev_write_frame(void *valve_data, int ignore_row_num) { - ssize_t size = write(fifo_dev_fd, valve_data, 32 * 600); - int res = -(size < 32 * 600); + ssize_t size; + char *valve_to_write = (char *)valve_data + ignore_row_num * HOST_COMPUTER_PICTURE_COLUMN_BYTES; + int size_to_write = HOST_COMPUTER_PICTURE_COLUMN_BYTES * (HOST_COMPUTER_PICTURE_ROW_NUM - ignore_row_num); + + size = write(fifo_dev_fd, valve_to_write, size_to_write); + int res = -(size < size_to_write); ON_ERROR_RET(res, "size=", "", -1); return 0; @@ -71,7 +101,6 @@ int fifo_dev_write_row(void *valve_data) int fifo_dev_clear() { int res = ioctl(fifo_dev_fd, _IOW('D', FIFO_CMD_FUNCTION_CLEAR, 0)); - ON_ERROR_RET(res, "", "", -1); return 0; } @@ -84,7 +113,7 @@ int fifo_dev_clear() */ int fifo_dev_write_delay(uint32_t count) { - int res = ioctl(fifo_dev_fd, _IOW('D', FIFO_CMD_FUNCTION_CLEAR, 0), count); + int res = ioctl(fifo_dev_fd, _IOW('D', FIFO_CMD_FUNCTION_PADDING, 0), count); ON_ERROR_RET(res, "", "", -1); return 0; @@ -93,7 +122,7 @@ int fifo_dev_write_delay(uint32_t count) /** * @brief Get the count of items in the hardware fifo. * @note An item from hardware fifo is of 256 bits in size, aka. 32 bytes, 8 integers - * @return 0 - success, other - error + * @return >=0 - success, other - error */ int fifo_dev_get_count() { diff --git a/source/linux_app/fifo_dev.h b/source/linux_app/fifo_dev.h index ba230fd..8b0fbbd 100644 --- a/source/linux_app/fifo_dev.h +++ b/source/linux_app/fifo_dev.h @@ -1,12 +1,21 @@ /** * @file fifo_dev.h * @brief Operate the hardware fifo with Linux application - * @details Call fifo_dev_init() paired with fifo_dev_deinit() as their names imply, fifo_dev_write() can be executed several times to operate the hardware fifo between fifo_dev_init() and fifo_dev_deinit() + * @details Call fifo_dev_init() paired with fifo_dev_deinit() as their names imply, fifo_dev_write_xxx() can be executed several times to operate the hardware fifo between fifo_dev_init() and fifo_dev_deinit() * @mainpage github.com/NanjingForestryUniversity - * @author miaow - * @email 3703781@qq.com - * @version 1.0 - * @date 2022/06/09 + * @author miaow (3703781@qq.com) + * @version 1.1 + * @date 2022/08/07 + * @mainpage github.com/NanjingForestryUniversity + * + * @copyright Copyright (c) 2022 miaow + * + * @par Changelog: + * + *
Date Version Author Description + *
2022/06/09 1.0 miaow Write this file + *
2022/08/07 1.1 miaow Add ignore_row_num to fifo_dev_write_frame + *
*/ #ifndef __FIFO_DEV_H #define __FIFO_DEV_H @@ -16,7 +25,8 @@ #define FIFO_DEV_PATH "/dev/fifo" int fifo_dev_init(void); -int fifo_dev_write_frame(void *valve_data); +int fifo_dev_get_emptycount(void); +int fifo_dev_write_frame(void *valve_data, int ignore_row_num); int fifo_dev_clear(void); int fifo_dev_write_delay(uint32_t count); int fifo_dev_write_row(void *valve_data); diff --git a/source/linux_app/host_computer.c b/source/linux_app/host_computer.c index 41c0a51..8edaa0c 100644 --- a/source/linux_app/host_computer.c +++ b/source/linux_app/host_computer.c @@ -2,16 +2,17 @@ * @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 + * @version 1.1 + * @date 2022/08/06 * @mainpage github.com/NanjingForestryUniversity - * + * * @copyright Copyright (c) 2022 miaow - * + * * @par Changelog: * *
Date Version Author Description *
2022/01/16 1.0 miaow Write this file + *
2022/08/06 1.1 miaow Add fifob *
*/ #include @@ -27,9 +28,7 @@ #include #include #include - -#define HOST_COMPUTER_PICTURE_COLUMN_BYTES (HOST_COMPUTER_PICTURE_COLUMN_NUM / 8) -#define HOST_COMPUTER_RAW_DATA_BYTES (HOST_COMPUTER_PICTURE_COLUMN_BYTES * HOST_COMPUTER_PICTURE_ROW_NUM) +#include static char perror_buffer[128]; /** @@ -51,7 +50,7 @@ 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 + * @return 0 - success */ int hostcomputer_init(queue_uint64_msg_t *cmd_q) { @@ -63,6 +62,12 @@ int hostcomputer_init(queue_uint64_msg_t *cmd_q) return 0; } +static void send_error(int fd) +{ + write(fd, "error", 5); + printf("\r\nerror sent\r\n"); +} + /** * @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 @@ -111,13 +116,18 @@ 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; + // printf("loop thread in %s start\r\n", __FILE__); + int need_exit = 0, frame_count = 0, error_sent = 0; + int std_count, empty_packets_num = 0; + int empty_count_initial = 0; + int empty_count_processed = 0; char pre; uint16_t n_bytes; char type[2]; - char data[HOST_COMPUTER_RAW_DATA_BYTES + 1]; + char data[HOST_COMPUTER_PICTURE_BYTES + 1]; char check[2]; + datafilter_typedef datafilter; + while (!need_exit) { pthread_mutex_lock(&_global_structure.loop_thread_mutex); @@ -169,10 +179,10 @@ void *loop_thread_func(void *param) continue; } n_bytes = ntohs(n_bytes); - if (n_bytes != HOST_COMPUTER_RAW_DATA_BYTES + 2 && n_bytes > 10) + if (n_bytes != HOST_COMPUTER_PICTURE_BYTES + 2 && n_bytes > 10) { // close(_global_structure.socket_fd); - printf("n_bytes> 10 and n_bytes!=HOST_COMPUTER_RAW_DATA_BYTES + 2\r\n"); + printf("n_bytes> 10 and n_bytes!=HOST_COMPUTER_PICTURE_BYTES + 2\r\n"); continue; } if (recvn(_global_structure.socket_fd, (char *)type, 2) != 2) @@ -212,17 +222,94 @@ void *loop_thread_func(void *param) // 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 != HOST_COMPUTER_RAW_DATA_BYTES) + /* + int current_count = fifo_dev_get_count(); + int current_count_filtered = datafilter_calculate(&datafilter_a, current_count); + + if (++frame_count_a > HOST_COMPUTER_BEGINNING_PICTURES_IGNORE_NUM) { - printf("n_bytes-2!=%d\r\n", HOST_COMPUTER_RAW_DATA_BYTES); + fifo_dev_write_frame(data, 0); + } + int added_count = fifo_dev_get_count(); + printf("before %d->after %d, diff %d, filter %d\r\n", current_count, added_count, added_count - current_count, current_count_filtered); + */ + //================================================= + + int current_count, current_count_filtered, diff_count, empty_count_to_process; + if (n_bytes - 2 != HOST_COMPUTER_PICTURE_BYTES) + { + printf("n_bytes-2!=%d\r\n", HOST_COMPUTER_PICTURE_BYTES); continue; } - fifo_dev_write_frame(data); + // get the item counts and its slide average value + current_count = fifo_dev_get_count(); + current_count_filtered = datafilter_calculate(&datafilter, current_count); + frame_count++; + if (frame_count == HOST_COMPUTER_PICTURES_BEGINNING_IGNORE_NUM + 1) + { + empty_count_initial = fifo_dev_get_emptycount(); + } + else if (frame_count == 100) // record the normal item counts in fifo + { + std_count = current_count_filtered; + } + if (frame_count > HOST_COMPUTER_PICTURES_BEGINNING_IGNORE_NUM) + { + // do nothing at first two frames, because that the first frame is set to zero and was concatenated to the delay frame before + // in case of late arrival of the first two frames. + empty_count_to_process = fifo_dev_get_emptycount() - empty_count_initial - empty_count_processed; + if (empty_count_to_process >= HOST_COMPUTER_PICTURE_ROW_NUM) + { + empty_count_processed += HOST_COMPUTER_PICTURE_ROW_NUM; + } + else + { + fifo_dev_write_frame(data, empty_count_to_process); + empty_count_processed += empty_count_to_process; + } + } + if (current_count == 0) + empty_packets_num++; + else + empty_packets_num = 0; + + + // print fifo status + printf("a ||| %d | cnt %d | avgcnt %d | stdcnt %d", + frame_count, current_count, current_count_filtered, std_count); + fflush(stdout); + // if (empty_count_to_process) + printf(" ||| initemp %d | toprc %d | prcd %d\r\n", empty_count_initial, + empty_count_to_process, empty_count_processed); + // else + // printf("\r\n"); + // if the item counts changes a lot compared with normal counts, + // meaning something goes wrong, a message will send to the hostcomputer + diff_count = current_count_filtered - std_count; + int diff_cond = diff_count > 250 || diff_count < -250; + int frame_count_cond = frame_count > 100; + int empty_packets_cond = empty_packets_num >= 5; + + if (((frame_count_cond && diff_cond) || empty_packets_cond) && !error_sent) + { + error_sent = 1; + printf("\r\na ||| avgcnt %d | %d larger", current_count_filtered, diff_count); + fflush(stdout); + 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)); + frame_count = 0; + error_sent = 0; + empty_packets_num = 0; + std_count = 0; + datafilter_deinit(&datafilter); + datafilter_init(&datafilter, 20); + empty_count_processed = 0; + empty_count_initial = 0; + queue_uint64_put(_global_structure.cmd_q, (atoll(data) << 32) | HOSTCOMPUTER_CMD_START); } else if (type[0] == 's' && type[1] == 'p') @@ -255,15 +342,11 @@ void *loop_thread_func(void *param) // 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') + 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; @@ -271,7 +354,7 @@ void *loop_thread_func(void *param) /** * @brief Deinitialize and release resources used by host computer module - * @return int + * @return int */ int hostcomputer_deinit() { diff --git a/source/linux_app/host_computer.h b/source/linux_app/host_computer.h index a523222..00cabf0 100644 --- a/source/linux_app/host_computer.h +++ b/source/linux_app/host_computer.h @@ -2,8 +2,8 @@ * @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 + * @version 1.1 + * @date 2022/08/6 * * @copyright Copyright (c) 2022 miaow * @@ -11,6 +11,7 @@ * *
Date Version Author Description *
2022/01/16 1.0 miaow Write this file + *
2022/08/06 1.1 miaow Add fifob *
*/ #ifndef __HOST_COMPUTER_H @@ -22,11 +23,14 @@ #define HOST_COMPUTER_IP "192.168.10.8" #define HOST_COMPUTER_PORT 13452 -#define HOST_COMPUTER_PICTURE_ROW_NUM 600 +#define HOST_COMPUTER_PICTURE_ROW_NUM 1024 #define HOST_COMPUTER_PICTURE_COLUMN_NUM 256 +#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) +#define HOST_COMPUTER_PICTURES_BEGINNING_IGNORE_NUM 1 /** - * @brief The commonds, ref hostcomputer通信协议 v1.1.md + * @brief The commonds, ref 通信协议 */ enum HOSTCOMPUTER_CMD { @@ -38,7 +42,6 @@ enum HOSTCOMPUTER_CMD HOSTCOMPUTER_CMD_SETVALVETRIGPULSECOUNT = 7, HOSTCOMPUTER_CMD_SETCAMERATOVALVEPULSECOUNT = 8, HOSTCOMPUTER_CMD_STOP_TEST = 9 - }; int hostcomputer_init(queue_uint64_msg_t *cmd_q); diff --git a/source/linux_app/main.c b/source/linux_app/main.c index 8bfd955..eff06ab 100644 --- a/source/linux_app/main.c +++ b/source/linux_app/main.c @@ -5,9 +5,9 @@ * @version 1.0 * @date 2022/06/12 * @mainpage github.com/NanjingForestryUniversity - * + * * @copyright Copyright (c) 2022 miaow - * + * * @par Changelog: * *
Date Version Author Description @@ -25,9 +25,12 @@ #include #include -#define SET_VALVE_ONLY_N_ON(u32_buf, n) bzero(u32_buf, sizeof(u32_buf));SET_VALVE_N_ON(u32_buf, n) +#define SET_VALVE_ONLY_N_ON(u32_buf, n) \ + bzero(u32_buf, sizeof(u32_buf)); \ + SET_VALVE_N_ON(u32_buf, n) #define SET_VALVE_N_ON(u32_buf, n) u32_buf[n / 32] = 1 << (n % 32) + /** * @brief Value of state machine */ @@ -42,7 +45,7 @@ queue_uint64_msg_t cmd_queue = {0}; static status_enum_t status = SLEEPING; static int camera_trigger_pulse_count = 1200; static int valve_trigger_pulse_count = 120; -static int camera_to_valve_pulse_count = 5000; +static int camera_to_valve_pulse_count = 500; void process_cmd(uint64_t *cmd); @@ -59,8 +62,40 @@ int main(int argc, char *argv[]) // Initialize drivers and clear all caches encoder_dev_init(); encoder_dev_set_trigmod(ENCODER_TRIG_MODE_INTERNEL); - encoder_dev_set_divide(2, 2); + encoder_dev_set_divide(2, 8); fifo_dev_init(); + + //==test encoder================================================================ + // fifo_dev_init(); + // encoder_dev_set_trigmod(ENCODER_TRIG_MODE_EXTERNEL); + // encoder_dev_set_divide(4, 8); + // int a = 1; + // while (a) + // { + // printf("input a\r\n"); + // scanf("%d", &a); + // printf("a=%d\r\n\r\n\r\n", a); + // } + // encoder_dev_set_trigmod(ENCODER_TRIG_MODE_INTERNEL); + // encoder_dev_deinit(); + // queue_uint64_deinit(&cmd_queue); + // return 0; + + //==test fifo================================================================ + // char data[HOST_COMPUTER_PICTURE_COLUMN_BYTES * HOST_COMPUTER_PICTURE_ROW_NUM + 1]; + // fifo_dev_init(); + // fifo_dev_write_frame(data); + // printf("%d\r\n", fifo_dev_get_count()); + // fifo_dev_write_frame(data); + // printf("%d\r\n", fifo_dev_get_count()); + // fifo_dev_clear(); + // printf("%d\r\n", fifo_dev_get_count()); + // fifo_dev_deinit(); + // encoder_dev_deinit(); + // return 0; + //================================================================== + + fifo_dev_clear(); hostcomputer_init(&cmd_queue); printf("\r\n>>>>>\r\nstatus==SLEEPING\r\n<<<<<\r\n\r\n"); uint64_t cmd; @@ -70,20 +105,20 @@ int main(int argc, char *argv[]) { if (queue_uint64_get(&cmd_queue, &cmd) == 0) process_cmd(&cmd); - usleep(100000); + usleep(1000); } // Never run here hostcomputer_deinit(); fifo_dev_clear(); - encoder_dev_set_divide(2, 2); - encoder_dev_virtual_trig(10); + encoder_dev_set_divide(2, 8); + encoder_dev_virtual_trig(20); fifo_dev_deinit(); encoder_dev_set_trigmod(ENCODER_TRIG_MODE_INTERNEL); encoder_dev_deinit(); queue_uint64_deinit(&cmd_queue); - + return 0; } @@ -91,24 +126,27 @@ int main(int argc, char *argv[]) * @brief Excute the command and control the states * @param cmd The command to be excuted */ -void process_cmd(uint64_t *cmd) +void process_cmd(uint64_t *cmd) { int tmp_cmd = (int)*cmd; int tmp_data = (int)(*cmd >> 32); - // Only in the SLEEPING state, it resbonds to START or TEST command. + // Only in the SLEEPING state, it resbonds to START or TEST command. if (status == SLEEPING) { if (tmp_cmd == HOSTCOMPUTER_CMD_START) { // Before running, clear the hardware fifo and hardware encoder. Then, the two dividers and delay value should be set. - // Also, the hareware encoder is expected to receiving pluse of encoder: the EXTERNAL mode + // Also, the hareware encoder is expected to receiving pluse of encoder: the EXTERNAL mode fifo_dev_clear(); - fifo_dev_write_delay(camera_to_valve_pulse_count); encoder_dev_flush(); + encoder_dev_set_divide(valve_trigger_pulse_count, camera_trigger_pulse_count); + fifo_dev_write_delay(camera_to_valve_pulse_count + HOST_COMPUTER_PICTURE_ROW_NUM * HOST_COMPUTER_PICTURES_BEGINNING_IGNORE_NUM); encoder_dev_set_trigmod(ENCODER_TRIG_MODE_EXTERNEL); - printf("\r\n>>>>>\r\nstatus==RUNNING\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); + printf("\r\n>>>>>\r\nstatus==RUNNING\r\ncamera_trigger_pulse_count=%d\r\nvalve_trigger_pulse_count=%d\r\n" + "camera_to_valve_pulse_count=%d\r\n<<<<<\r\n\r\n", camera_trigger_pulse_count, + valve_trigger_pulse_count, camera_to_valve_pulse_count + HOST_COMPUTER_PICTURE_ROW_NUM * HOST_COMPUTER_PICTURES_BEGINNING_IGNORE_NUM); status = RUNNING; } else if (tmp_cmd == HOSTCOMPUTER_CMD_TEST) @@ -120,16 +158,16 @@ void process_cmd(uint64_t *cmd) fifo_dev_clear(); encoder_dev_flush(); encoder_dev_set_trigmod(ENCODER_TRIG_MODE_INTERNEL); - encoder_dev_set_divide(2, 2); + encoder_dev_set_divide(2, 8); // fifo out every 8/4=2 cycle, valveboard operate every 2 cycle // A parameter below 256 represents a single shot, the value of parameter indicates the valve to triggle. if (tmp_data < 256) { SET_VALVE_ONLY_N_ON(row_data, tmp_data); fifo_dev_write_row(row_data); - // delay for 100 ms and turn off the valve + // delay for 15000 us and turn off the valve encoder_dev_virtual_trig(2); - usleep(100000); + usleep(15000); encoder_dev_virtual_trig(2); } // 257 represents triggle valve from NO.1 to 256 sequenctially. This loop blocks for 25.7s. @@ -139,11 +177,17 @@ void process_cmd(uint64_t *cmd) { SET_VALVE_ONLY_N_ON(row_data, i); fifo_dev_write_row(row_data); + bzero(&row_data, sizeof(row_data)); + fifo_dev_write_row(row_data); + // printf("%d,%d\r\n", fifo_dev_get_count(), fifob_dev_get_count()); } for (int i = 0; i < 257; i++) { encoder_dev_virtual_trig(2); - usleep(100000); + usleep(15000); + encoder_dev_virtual_trig(2); + usleep(100000 - 15000); + // printf("%d,%d\r\n", fifo_dev_get_count(), fifob_dev_get_count()); } } } @@ -164,14 +208,14 @@ void process_cmd(uint64_t *cmd) else if (status == RUNNING) { if (tmp_cmd == HOSTCOMPUTER_CMD_STOP) - { + { // Clear hardware fifo. // 10 virtual triggles in internal mode ensure valve is turned off. // Hardware encoder is flushed for a fresh start. fifo_dev_clear(); encoder_dev_set_trigmod(ENCODER_TRIG_MODE_INTERNEL); - encoder_dev_set_divide(2, 2); - encoder_dev_virtual_trig(10); + encoder_dev_set_divide(4, 4); + encoder_dev_virtual_trig(20); encoder_dev_flush(); status = SLEEPING; printf("\r\n>>>>>\r\nstatus==SLEEPING\r\n<<<<<\r\n\r\n"); diff --git a/source/linux_app/main.h b/source/linux_app/main.h new file mode 100644 index 0000000..e69de29 diff --git a/source/linux_app/queue_uint64.c b/source/linux_app/queue_uint64.c index fecac84..b49d43c 100644 --- a/source/linux_app/queue_uint64.c +++ b/source/linux_app/queue_uint64.c @@ -7,9 +7,9 @@ * @version 1.0 * @date 2021/01/10 * @mainpage github.com/NanjingForestryUniversity - * + * * @copyright Copyright (c) 2022 miaow - * + * * @par Changelog: * *
Date Version Author Description @@ -114,8 +114,11 @@ int queue_uint64_init(queue_uint64_msg_t *q, int max_count) */ int queue_uint64_deinit(queue_uint64_msg_t *q) { - free(q->buffer); - q->buffer = NULL; + if (q->buffer != NULL) + { + free(q->buffer); + q->buffer = NULL; + } pthread_mutex_destroy(&q->_mux); // pthread_cond_destroy(&q->_cond_get); // pthread_cond_destroy(&q->_cond_put); diff --git a/source/linux_app/version b/source/linux_app/version index 9f8e9b6..ea710ab 100644 --- a/source/linux_app/version +++ b/source/linux_app/version @@ -1 +1 @@ -1.0 \ No newline at end of file +1.2 \ No newline at end of file diff --git a/source/linux_driver/encoder.c b/source/linux_driver/encoder.c index 65e66c8..c66484a 100644 --- a/source/linux_driver/encoder.c +++ b/source/linux_driver/encoder.c @@ -9,7 +9,7 @@ #include #include -#define ENCODER_CNT 1 /* 主设备号 */ +#define ENCODER_CNT 1 /* 主设备号 */ #define ENCODER_NAME "encoder" /* 设备名字 */ #define ENCODER_CMD_FUNCTION_CLEAR 1 @@ -18,7 +18,6 @@ #define ENCODER_CMD_INPUT_MODE_EXTERNEL 100 #define ENCODER_CMD_INPUT_MODE_INTERNEL 101 - /* * 相关寄存器地址定义 */ @@ -28,26 +27,30 @@ #define ENCODER_REG_2_OFFSET 0x00000008 #define ENCODER_REG_3_OFFSET 0x0000000C +#define ENCODER_CR_VTS_MASK ((u32)(1 << 2)) // 内部触发信号 (Virtual Triggle Signal) MOD位置1时,由上位机软件写入,将该位信号作为触发信号 +#define ENCODER_CR_MOD_MASK ((u32)(1 << 1)) // 模式选择 (Mode) 0: 外部触发模式,外部触发编码器转动, 1: 内部触发模式,上位机软件模拟触发信号 +#define ENCODER_CR_CLR_MASK ((u32)(1 << 0)) // 清除缓存 (Clear) 清除编码和分频控制器内部的分频计数值,不影响VDIV和CDIV + /* 映射后的寄存器虚拟地址指针 */ -static void __iomem *encoder_reg_0_addr; -static void __iomem *encoder_reg_1_addr; -static void __iomem *encoder_reg_2_addr; -static void __iomem *encoder_reg_3_addr; +static void __iomem *encoder_cr_addr; +static void __iomem *encoder_vdivr_addr; +static void __iomem *encoder_cdivr_addr; +static void __iomem *encoder_reg3_addr; struct encoder_dev { - dev_t devid; /* 设备号 */ - struct cdev cdev; /* cdev */ - struct class *class; /* 类 */ - struct device *device; /* 设备 */ - int major; /* 主设备号 */ - int minor; /* 次设备号 */ + dev_t devid; /* 设备号 */ + struct cdev cdev; /* cdev */ + struct class *class; /* 类 */ + struct device *device; /* 设备 */ + int major; /* 主设备号 */ + int minor; /* 次设备号 */ }; typedef struct { - u32 valve_divide_value; - u32 camera_divide_value; + u32 valve_divide_value; + u32 camera_divide_value; } kernelbuf_typedef; static struct encoder_dev encoder; @@ -61,7 +64,7 @@ static struct encoder_dev encoder; */ static int encoder_open(struct inode *inode, struct file *filp) { - return 0; + return 0; } /* @@ -74,38 +77,43 @@ static int encoder_open(struct inode *inode, struct file *filp) */ static ssize_t encoder_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { - int ret; - - u32 data; - kernelbuf_typedef kern_buf = { - .valve_divide_value = 0, - .camera_divide_value = 0, - }; - if (cnt != sizeof(kern_buf)) - { - printk(KERN_ERR "encoder write: cnt error, cnt=%d", cnt); - return -EFAULT; - } - ret = copy_from_user(&kern_buf, buf, cnt); // 得到应用层传递过来的数据 - if (ret < 0) - { - printk(KERN_ERR "kernel write failed!\r\n"); - return -EFAULT; - } - if (!(kern_buf.valve_divide_value || kern_buf.camera_divide_value)) - return 0; - - data = readl(encoder_reg_0_addr); - writel(data & ~(u32)(1 << 0), encoder_reg_0_addr); + int ret; - if (kern_buf.valve_divide_value != 0) - writel(kern_buf.valve_divide_value, encoder_reg_1_addr); - if (kern_buf.camera_divide_value != 0) - writel(kern_buf.camera_divide_value, encoder_reg_2_addr); + u32 data; + kernelbuf_typedef kern_buf = { + .valve_divide_value = 0, + .camera_divide_value = 0, + }; + if (cnt != sizeof(kern_buf)) + { + printk(KERN_ERR "encoder write: cnt error, cnt=%d", cnt); + return -EFAULT; + } + ret = copy_from_user(&kern_buf, buf, cnt); // 得到应用层传递过来的数据 + if (ret < 0) + { + printk(KERN_ERR "kernel write failed!\r\n"); + return -EFAULT; + } + // 最小分频值为4 + if (kern_buf.valve_divide_value < 2 || kern_buf.camera_divide_value < 4) + { + return -EFAULT; + } - writel(data | (u32)(1 << 0), encoder_reg_0_addr); + // 写入0后清除ENCODER内部计数器缓存清除 + data = readl(encoder_cr_addr); + writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr); - return cnt; + if (kern_buf.valve_divide_value != 0) + writel(kern_buf.valve_divide_value, encoder_vdivr_addr); + if (kern_buf.camera_divide_value != 0) + writel(kern_buf.camera_divide_value, encoder_cdivr_addr); + + // 写入1退出清除状态,使得ENCODER内部计数器能正常工作,内部计数器在正常工作前已经被清零 + writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr); + + return cnt; } /* @@ -115,158 +123,163 @@ static ssize_t encoder_write(struct file *filp, const char __user *buf, size_t c */ static int encoder_release(struct inode *inode, struct file *filp) { - return 0; + return 0; } static long encoder_ioctl(struct file *fp, unsigned int cmd, unsigned long tmp) { - u32 data, cmd_parsed; - if (_IOC_TYPE(cmd) != 'D' || _IOC_DIR(cmd) != _IOC_WRITE) - { - printk(KERN_ERR "IOC_TYPE or IOC_WRITE error: IOC_TYPE=%c, IOC_WRITE=%d\r\n", _IOC_TYPE(cmd), _IOC_DIR(cmd)); - return -EINVAL; - } - cmd_parsed = _IOC_NR(cmd); - data = readl(encoder_reg_0_addr); - if (cmd_parsed == ENCODER_CMD_FUNCTION_CLEAR) - { - writel(data & ~(u32)(1 << 0), encoder_reg_0_addr); - writel(data | (u32)(1 << 0), encoder_reg_0_addr); - } - else if (cmd_parsed == ENCODER_CMD_INPUT_MODE_EXTERNEL) - { - writel(data & ~(u32)(1 << 1), encoder_reg_0_addr); - } - else if (cmd_parsed == ENCODER_CMD_INPUT_MODE_INTERNEL) - { - writel(data | (u32)(1 << 1), encoder_reg_0_addr); - } - else if (cmd_parsed == ENCODER_CMD_FUNCTION_VIRT_INPUT) - { - int i; - // 1. ENCODER_CMD_INPUT_MODE_INTERNEL - writel(data | (u32)(1 << 1), encoder_reg_0_addr); - // 2. Generate pluses - for (i = 0; i < tmp; i++) - { - writel(data & ~(u32)(1 << 2), encoder_reg_0_addr); - writel(data | (u32)(1 << 2), encoder_reg_0_addr); - } - // 3. Recover the original configuration - writel(data, encoder_reg_0_addr); - } + u32 data, cmd_parsed; + if (_IOC_TYPE(cmd) != 'D' || _IOC_DIR(cmd) != _IOC_WRITE) + { + printk(KERN_ERR "IOC_TYPE or IOC_WRITE error: IOC_TYPE=%c, IOC_WRITE=%d\r\n", _IOC_TYPE(cmd), _IOC_DIR(cmd)); + return -EINVAL; + } + cmd_parsed = _IOC_NR(cmd); + data = readl(encoder_cr_addr); + if (cmd_parsed == ENCODER_CMD_FUNCTION_CLEAR) + { + writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr); // 写入0后清除ENCODER内部计数器缓存清除 + writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr); // 写入1退出清除状态,使得ENCODER内部计数器能正常工作,内部计数器在正常工作前已经被清零 + } + else if (cmd_parsed == ENCODER_CMD_INPUT_MODE_EXTERNEL) + { + // 设为外部触发模式 + writel(data & ~ENCODER_CR_MOD_MASK, encoder_cr_addr); + } + else if (cmd_parsed == ENCODER_CMD_INPUT_MODE_INTERNEL) + { + // 设为内部触发模式 + writel(data | (u32)(1 << 1), encoder_cr_addr); + } + else if (cmd_parsed == ENCODER_CMD_FUNCTION_VIRT_INPUT) + { + int i; + // 虚拟触发,tmp为周期数 + // 1. 设为内部触发模式 + writel(data | (u32)(1 << 1), encoder_cr_addr); - return 0; + // 2. 产生虚拟的高低电平 + for (i = 0; i < tmp; i++) + { + writel(data & ~ENCODER_CR_VTS_MASK, encoder_cr_addr); + writel(data | ENCODER_CR_VTS_MASK, encoder_cr_addr); + } + + // 3. 恢复为原来的状态和模式 + writel(data, encoder_cr_addr); + } + + return 0; } /* 设备操作函数 */ static struct file_operations encoder_fops = { - .owner = THIS_MODULE, - .open = encoder_open, - .write = encoder_write, - .release = encoder_release, - .unlocked_ioctl = encoder_ioctl, + .owner = THIS_MODULE, + .open = encoder_open, + .write = encoder_write, + .release = encoder_release, + .unlocked_ioctl = encoder_ioctl, }; static int __init encoder_init(void) { - int ret; - u32 data; - /* 寄存器地址映射 */ - encoder_reg_0_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_0_OFFSET, 4); - encoder_reg_1_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_1_OFFSET, 4); - encoder_reg_2_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_2_OFFSET, 4); - encoder_reg_3_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_3_OFFSET, 4); + int ret; + u32 data; + /* 寄存器地址映射 */ + encoder_cr_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_0_OFFSET, 4); + encoder_vdivr_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_1_OFFSET, 4); + encoder_cdivr_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_2_OFFSET, 4); + encoder_reg3_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_3_OFFSET, 4); - /* 注册字符设备驱动 */ - //(1)创建设备号 - if (encoder.major) - { - encoder.devid = MKDEV(encoder.major, 0); - ret = register_chrdev_region(encoder.devid, ENCODER_CNT, ENCODER_NAME); - if (ret) - goto FAIL_REGISTER_CHR_DEV; - } - else - { - ret = alloc_chrdev_region(&encoder.devid, 0, ENCODER_CNT, ENCODER_NAME); - if (ret) - goto FAIL_REGISTER_CHR_DEV; - encoder.major = MAJOR(encoder.devid); - encoder.minor = MINOR(encoder.devid); - } + /* 注册字符设备驱动 */ + //(1)创建设备号 + if (encoder.major) + { + encoder.devid = MKDEV(encoder.major, 0); + ret = register_chrdev_region(encoder.devid, ENCODER_CNT, ENCODER_NAME); + if (ret) + goto FAIL_REGISTER_CHR_DEV; + } + else + { + ret = alloc_chrdev_region(&encoder.devid, 0, ENCODER_CNT, ENCODER_NAME); + if (ret) + goto FAIL_REGISTER_CHR_DEV; + encoder.major = MAJOR(encoder.devid); + encoder.minor = MINOR(encoder.devid); + } - //(2)初始化cdev - encoder.cdev.owner = THIS_MODULE; - cdev_init(&encoder.cdev, &encoder_fops); + //(2)初始化cdev + encoder.cdev.owner = THIS_MODULE; + cdev_init(&encoder.cdev, &encoder_fops); - //(3)添加cdev - ret = cdev_add(&encoder.cdev, encoder.devid, ENCODER_CNT); - if (ret) - goto FAIL_ADD_CDEV; + //(3)添加cdev + ret = cdev_add(&encoder.cdev, encoder.devid, ENCODER_CNT); + if (ret) + goto FAIL_ADD_CDEV; - //(4)创建类 - encoder.class = class_create(THIS_MODULE, ENCODER_NAME); - if (IS_ERR(encoder.class)) - { - ret = PTR_ERR(encoder.class); - goto FAIL_CREATE_CLASS; - } + //(4)创建类 + encoder.class = class_create(THIS_MODULE, ENCODER_NAME); + if (IS_ERR(encoder.class)) + { + ret = PTR_ERR(encoder.class); + goto FAIL_CREATE_CLASS; + } - //(5)创建设备 - encoder.device = device_create(encoder.class, NULL, encoder.devid, NULL, ENCODER_NAME); - if (IS_ERR(encoder.device)) - { - ret = PTR_ERR(encoder.device); - goto FAIL_CREATE_DEV; - } + //(5)创建设备 + encoder.device = device_create(encoder.class, NULL, encoder.devid, NULL, ENCODER_NAME); + if (IS_ERR(encoder.device)) + { + ret = PTR_ERR(encoder.device); + goto FAIL_CREATE_DEV; + } - //默认分频系数1000 - data = readl(encoder_reg_0_addr); - writel(data & ~(u32)(1 << 0), encoder_reg_0_addr); - writel(1000, encoder_reg_1_addr); - writel(1000, encoder_reg_2_addr); - writel(data | (u32)(1 << 0), encoder_reg_0_addr); + //默认分频系数1000 + data = readl(encoder_cr_addr); + writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr); // 清除硬件计数器缓存 + writel(1000, encoder_vdivr_addr); // 设置阀触发分频 + writel(1000, encoder_cdivr_addr); // 设置相机触发分频 + writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr); // 清除完毕 - return 0; + return 0; FAIL_CREATE_DEV: - class_destroy(encoder.class); + class_destroy(encoder.class); FAIL_CREATE_CLASS: - cdev_del(&encoder.cdev); + cdev_del(&encoder.cdev); FAIL_ADD_CDEV: - unregister_chrdev_region(encoder.devid, ENCODER_CNT); + unregister_chrdev_region(encoder.devid, ENCODER_CNT); FAIL_REGISTER_CHR_DEV: - iounmap(encoder_reg_0_addr); - iounmap(encoder_reg_1_addr); - iounmap(encoder_reg_2_addr); - iounmap(encoder_reg_3_addr); + iounmap(encoder_cr_addr); + iounmap(encoder_vdivr_addr); + iounmap(encoder_cdivr_addr); + iounmap(encoder_reg3_addr); - return ret; + return ret; } static void __exit encoder_exit(void) { - //(1)注销设备 - device_destroy(encoder.class, encoder.devid); + //(1)注销设备 + device_destroy(encoder.class, encoder.devid); - //(2)注销类 - class_destroy(encoder.class); + //(2)注销类 + class_destroy(encoder.class); - //(3)删除cdev - cdev_del(&encoder.cdev); + //(3)删除cdev + cdev_del(&encoder.cdev); - //(4)注销设备号 - unregister_chrdev_region(encoder.devid, ENCODER_CNT); + //(4)注销设备号 + unregister_chrdev_region(encoder.devid, ENCODER_CNT); - //(5)取消内存映射 - iounmap(encoder_reg_0_addr); - iounmap(encoder_reg_1_addr); - iounmap(encoder_reg_2_addr); - iounmap(encoder_reg_3_addr); + //(5)取消内存映射 + iounmap(encoder_cr_addr); + iounmap(encoder_vdivr_addr); + iounmap(encoder_cdivr_addr); + iounmap(encoder_reg3_addr); } /* 驱动模块入口和出口函数注册 */ @@ -274,4 +287,4 @@ module_init(encoder_init); module_exit(encoder_exit); MODULE_AUTHOR("DingKun"); MODULE_DESCRIPTION("driver for hardware encoder in the platform"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/source/linux_driver/fifo.c b/source/linux_driver/fifo.c index f230af8..703219d 100644 --- a/source/linux_driver/fifo.c +++ b/source/linux_driver/fifo.c @@ -14,6 +14,7 @@ #define FIFO_NAME "fifo" /* 名字 */ #define FIFO_CMD_FUNCTION_CLEAR 1 #define FIFO_CMD_FUNCTION_PADDING 2 +#define FIFO_CMD_GET_EMPTYCOUNT 3 /* * 相关寄存器地址定义 @@ -35,22 +36,34 @@ #define FIFO_REG_13_OFFSET 0x00000034 #define FIFO_REG_14_OFFSET 0x00000038 +#define FIFO_CR_CLR_MASK ((u32)(1 << 1)) // 清空队列 (Clear) 对该位写入1,队列将清空,同时队列输出为全0。注意:不要写入除1以外的任何值。 +#define FIFO_CR_WS_MASK ((u32)(1 << 0)) // 写入同步 (Write Synchronization) 对该位写入1,FIFO_DATx的数据按字节小端序进入队列。 注意:不要写入除1以外的任何值。 + +#define FIFO_SR_VLD_MASK ((u32)(1 << 16)) // 数据输出有效标志 (Valid) 0: 当前无有效输出,输出保持上一状态 1: 当前队列正在输出有效数据 +#define FIFO_SR_AMEM_MASK ((u32)(1 << 15)) // 队列将空标志 (Almost Empty) 0: 队列没有被读空 1: 队列在一个读时钟周期后会被读空 +#define FIFO_SR_EM_MASK ((u32)(1 << 14)) // 队列空标志 (Empty) 0: 队列中存在有效数据,没有被读空 1: 队列中已经没有有效数据 +#define FIFO_SR_AMFU_MASK ((u32)(1 << 13)) // 队列将满标志 (Almost Full) 0: 队列没有被写满 1: 队列在一个写时钟周期后会被写满 +#define FIFO_SR_FU_MASK ((u32)(1 << 12)) // 队列满标志 (Almost Full) 0: 队列中的有效数据小于FIFO数据深度 1: 队列中的有效数据达到FIFO数据深度 +#define FIFO_SR_CNT_MASK ((u32)(0xFFF << 0)) // 队列数据数量 (Data Count) 该值指示队列中的数据数量 注意:一个数据为384位宽 + +#define FIFO_ECR_CNT_MASK ((u32)0xFFFFFFFF) // 队列空读取累计次数 + /* 映射后的寄存器虚拟地址指针 */ -static void __iomem *fifo_reg_0_addr; -static void __iomem *fifo_reg_1_addr; -static void __iomem *fifo_reg_2_addr; -static void __iomem *fifo_reg_3_addr; -static void __iomem *fifo_reg_4_addr; -static void __iomem *fifo_reg_5_addr; -static void __iomem *fifo_reg_6_addr; -static void __iomem *fifo_reg_7_addr; -static void __iomem *fifo_reg_8_addr; -static void __iomem *fifo_reg_9_addr; -static void __iomem *fifo_reg_10_addr; -static void __iomem *fifo_reg_11_addr; -static void __iomem *fifo_reg_12_addr; -static void __iomem *fifo_reg_13_addr; -static void __iomem *fifo_reg_14_addr; +static void __iomem *fifo_dat0_addr; +static void __iomem *fifo_dat1_addr; +static void __iomem *fifo_dat2_addr; +static void __iomem *fifo_dat3_addr; +static void __iomem *fifo_dat4_addr; +static void __iomem *fifo_dat5_addr; +static void __iomem *fifo_dat6_addr; +static void __iomem *fifo_dat7_addr; +static void __iomem *fifo_dat8_addr; +static void __iomem *fifo_dat9_addr; +static void __iomem *fifo_dat10_addr; +static void __iomem *fifo_dat11_addr; +static void __iomem *fifo_sr_addr; +static void __iomem *fifo_ecr_addr; +static void __iomem *fifo_cr_addr; /* fifo设备结构体 */ struct fifo_dev @@ -87,7 +100,7 @@ static int fifo_open(struct inode *inode, struct file *filp) */ static ssize_t fifo_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { - u32 data = readl(fifo_reg_12_addr) & 0xFFF; + u32 data = readl(fifo_sr_addr) & FIFO_SR_CNT_MASK; copy_to_user(buf, &data, 4); return cnt; } @@ -109,7 +122,7 @@ static ssize_t fifo_write(struct file *filp, const char __user *buf, size_t cnt, if (cnt % 32 != 0 || cnt > sizeof(kern_buf_u32)) { - printk(KERN_ERR "cnt error, cnt=%d\r\n", cnt); + printk(KERN_ERR "cnt error, cnt=%d, sizeof=%d\r\n", cnt, (u32)sizeof(kern_buf_u32)); return -1; } @@ -122,19 +135,19 @@ static ssize_t fifo_write(struct file *filp, const char __user *buf, size_t cnt, for (i = 0; i < (cnt / sizeof(u32)); i += 8) { - writel(kern_buf_u32[i], fifo_reg_0_addr); - writel(kern_buf_u32[i + 1], fifo_reg_1_addr); - writel(kern_buf_u32[i + 2], fifo_reg_2_addr); - writel(kern_buf_u32[i + 3], fifo_reg_3_addr); - writel(kern_buf_u32[i + 4], fifo_reg_4_addr); - writel(kern_buf_u32[i + 5], fifo_reg_5_addr); - writel(kern_buf_u32[i + 6], fifo_reg_6_addr); - writel(kern_buf_u32[i + 7], fifo_reg_7_addr); - writel(0, fifo_reg_8_addr); - writel(0, fifo_reg_9_addr); - writel(0, fifo_reg_10_addr); - writel(0, fifo_reg_11_addr); - writel(1, fifo_reg_14_addr); + writel(kern_buf_u32[i], fifo_dat0_addr); + writel(kern_buf_u32[i + 1], fifo_dat1_addr); + writel(kern_buf_u32[i + 2], fifo_dat2_addr); + writel(kern_buf_u32[i + 3], fifo_dat3_addr); + writel(kern_buf_u32[i + 4], fifo_dat4_addr); + writel(kern_buf_u32[i + 5], fifo_dat5_addr); + writel(kern_buf_u32[i + 6], fifo_dat6_addr); + writel(kern_buf_u32[i + 7], fifo_dat7_addr); + writel(0, fifo_dat8_addr); + writel(0, fifo_dat9_addr); + writel(0, fifo_dat10_addr); + writel(0, fifo_dat11_addr); + writel(FIFO_CR_WS_MASK, fifo_cr_addr); } return cnt; @@ -152,33 +165,45 @@ static int fifo_release(struct inode *inode, struct file *filp) static long fifo_ioctl(struct file *fp, unsigned int cmd, unsigned long tmp) { - if (_IOC_TYPE(cmd) != 'D' || _IOC_DIR(cmd) != _IOC_WRITE) + if (_IOC_TYPE(cmd) != 'D') { printk(KERN_ERR "IOC_TYPE or IOC_WRITE error: IOC_TYPE=%c, IOC_WRITE=%d\r\n", _IOC_TYPE(cmd), _IOC_DIR(cmd)); return -EINVAL; } - if (_IOC_NR(cmd) == FIFO_CMD_FUNCTION_CLEAR) + if (_IOC_NR(cmd) == FIFO_CMD_GET_EMPTYCOUNT) { - writel(((u32)1 << 1), fifo_reg_14_addr); + u32 empty_count = readl(fifo_ecr_addr) & FIFO_ECR_CNT_MASK; + printk("%d\r\n", empty_count); + if (copy_to_user((u32 *)tmp, &empty_count, 4) < 0) + { + printk(KERN_ERR "get empty count error\r\n"); + return -EINVAL; + } + } + else if (_IOC_NR(cmd) == FIFO_CMD_FUNCTION_CLEAR) + { + // 清空队列 + writel(FIFO_CR_CLR_MASK, fifo_cr_addr); } else if (_IOC_NR(cmd) == FIFO_CMD_FUNCTION_PADDING) { + // 对队列中添加tmp个数的0元素 int i; for (i = 0; i < tmp; i ++) { - writel((u32)0, fifo_reg_0_addr); - writel((u32)0, fifo_reg_1_addr); - writel((u32)0, fifo_reg_2_addr); - writel((u32)0, fifo_reg_3_addr); - writel((u32)0, fifo_reg_4_addr); - writel((u32)0, fifo_reg_5_addr); - writel((u32)0, fifo_reg_6_addr); - writel((u32)0, fifo_reg_7_addr); - writel((u32)0, fifo_reg_8_addr); - writel((u32)0, fifo_reg_9_addr); - writel((u32)0, fifo_reg_10_addr); - writel((u32)0, fifo_reg_11_addr); - writel((u32)1, fifo_reg_14_addr); + writel((u32)0, fifo_dat0_addr); + writel((u32)0, fifo_dat1_addr); + writel((u32)0, fifo_dat2_addr); + writel((u32)0, fifo_dat3_addr); + writel((u32)0, fifo_dat4_addr); + writel((u32)0, fifo_dat5_addr); + writel((u32)0, fifo_dat6_addr); + writel((u32)0, fifo_dat7_addr); + writel((u32)0, fifo_dat8_addr); + writel((u32)0, fifo_dat9_addr); + writel((u32)0, fifo_dat10_addr); + writel((u32)0, fifo_dat11_addr); + writel(FIFO_CR_WS_MASK, fifo_cr_addr); } } return 0; @@ -198,21 +223,21 @@ static int __init fifo_init(void) { int ret; /* 寄存器地址映射 */ - fifo_reg_0_addr = ioremap(FIFO_REG_BASE + FIFO_REG_0_OFFSET, 4); - fifo_reg_1_addr = ioremap(FIFO_REG_BASE + FIFO_REG_1_OFFSET, 4); - fifo_reg_2_addr = ioremap(FIFO_REG_BASE + FIFO_REG_2_OFFSET, 4); - fifo_reg_3_addr = ioremap(FIFO_REG_BASE + FIFO_REG_3_OFFSET, 4); - fifo_reg_4_addr = ioremap(FIFO_REG_BASE + FIFO_REG_4_OFFSET, 4); - fifo_reg_5_addr = ioremap(FIFO_REG_BASE + FIFO_REG_5_OFFSET, 4); - fifo_reg_6_addr = ioremap(FIFO_REG_BASE + FIFO_REG_6_OFFSET, 4); - fifo_reg_7_addr = ioremap(FIFO_REG_BASE + FIFO_REG_7_OFFSET, 4); - fifo_reg_8_addr = ioremap(FIFO_REG_BASE + FIFO_REG_8_OFFSET, 4); - fifo_reg_9_addr = ioremap(FIFO_REG_BASE + FIFO_REG_9_OFFSET, 4); - fifo_reg_10_addr = ioremap(FIFO_REG_BASE + FIFO_REG_10_OFFSET, 4); - fifo_reg_11_addr = ioremap(FIFO_REG_BASE + FIFO_REG_11_OFFSET, 4); - fifo_reg_12_addr = ioremap(FIFO_REG_BASE + FIFO_REG_12_OFFSET, 4); - fifo_reg_13_addr = ioremap(FIFO_REG_BASE + FIFO_REG_13_OFFSET, 4); - fifo_reg_14_addr = ioremap(FIFO_REG_BASE + FIFO_REG_14_OFFSET, 4); + fifo_dat0_addr = ioremap(FIFO_REG_BASE + FIFO_REG_0_OFFSET, 4); + fifo_dat1_addr = ioremap(FIFO_REG_BASE + FIFO_REG_1_OFFSET, 4); + fifo_dat2_addr = ioremap(FIFO_REG_BASE + FIFO_REG_2_OFFSET, 4); + fifo_dat3_addr = ioremap(FIFO_REG_BASE + FIFO_REG_3_OFFSET, 4); + fifo_dat4_addr = ioremap(FIFO_REG_BASE + FIFO_REG_4_OFFSET, 4); + fifo_dat5_addr = ioremap(FIFO_REG_BASE + FIFO_REG_5_OFFSET, 4); + fifo_dat6_addr = ioremap(FIFO_REG_BASE + FIFO_REG_6_OFFSET, 4); + fifo_dat7_addr = ioremap(FIFO_REG_BASE + FIFO_REG_7_OFFSET, 4); + fifo_dat8_addr = ioremap(FIFO_REG_BASE + FIFO_REG_8_OFFSET, 4); + fifo_dat9_addr = ioremap(FIFO_REG_BASE + FIFO_REG_9_OFFSET, 4); + fifo_dat10_addr = ioremap(FIFO_REG_BASE + FIFO_REG_10_OFFSET, 4); + fifo_dat11_addr = ioremap(FIFO_REG_BASE + FIFO_REG_11_OFFSET, 4); + fifo_sr_addr = ioremap(FIFO_REG_BASE + FIFO_REG_12_OFFSET, 4); + fifo_ecr_addr = ioremap(FIFO_REG_BASE + FIFO_REG_13_OFFSET, 4); + fifo_cr_addr = ioremap(FIFO_REG_BASE + FIFO_REG_14_OFFSET, 4); /* 注册字符设备驱动 */ //(1)创建设备号 @@ -269,21 +294,21 @@ FAIL_ADD_CDEV: unregister_chrdev_region(fifo.devid, FIFO_CNT); FAIL_REGISTER_CHR_DEV: - iounmap(fifo_reg_0_addr); - iounmap(fifo_reg_1_addr); - iounmap(fifo_reg_2_addr); - iounmap(fifo_reg_3_addr); - iounmap(fifo_reg_4_addr); - iounmap(fifo_reg_5_addr); - iounmap(fifo_reg_6_addr); - iounmap(fifo_reg_7_addr); - iounmap(fifo_reg_8_addr); - iounmap(fifo_reg_9_addr); - iounmap(fifo_reg_10_addr); - iounmap(fifo_reg_11_addr); - iounmap(fifo_reg_12_addr); - iounmap(fifo_reg_13_addr); - iounmap(fifo_reg_14_addr); + iounmap(fifo_dat0_addr); + iounmap(fifo_dat1_addr); + iounmap(fifo_dat2_addr); + iounmap(fifo_dat3_addr); + iounmap(fifo_dat4_addr); + iounmap(fifo_dat5_addr); + iounmap(fifo_dat6_addr); + iounmap(fifo_dat7_addr); + iounmap(fifo_dat8_addr); + iounmap(fifo_dat9_addr); + iounmap(fifo_dat10_addr); + iounmap(fifo_dat11_addr); + iounmap(fifo_sr_addr); + iounmap(fifo_ecr_addr); + iounmap(fifo_cr_addr); return ret; } @@ -304,21 +329,21 @@ static void __exit fifo_exit(void) unregister_chrdev_region(fifo.devid, FIFO_CNT); //(5)取消内存映射 - iounmap(fifo_reg_0_addr); - iounmap(fifo_reg_1_addr); - iounmap(fifo_reg_2_addr); - iounmap(fifo_reg_3_addr); - iounmap(fifo_reg_4_addr); - iounmap(fifo_reg_5_addr); - iounmap(fifo_reg_6_addr); - iounmap(fifo_reg_7_addr); - iounmap(fifo_reg_8_addr); - iounmap(fifo_reg_9_addr); - iounmap(fifo_reg_10_addr); - iounmap(fifo_reg_11_addr); - iounmap(fifo_reg_12_addr); - iounmap(fifo_reg_13_addr); - iounmap(fifo_reg_14_addr); + iounmap(fifo_dat0_addr); + iounmap(fifo_dat1_addr); + iounmap(fifo_dat2_addr); + iounmap(fifo_dat3_addr); + iounmap(fifo_dat4_addr); + iounmap(fifo_dat5_addr); + iounmap(fifo_dat6_addr); + iounmap(fifo_dat7_addr); + iounmap(fifo_dat8_addr); + iounmap(fifo_dat9_addr); + iounmap(fifo_dat10_addr); + iounmap(fifo_dat11_addr); + iounmap(fifo_sr_addr); + iounmap(fifo_ecr_addr); + iounmap(fifo_cr_addr); } /* 驱动模块入口和出口函数注册 */ diff --git a/source/linux_driver/version b/source/linux_driver/version index 9f8e9b6..ea710ab 100644 --- a/source/linux_driver/version +++ b/source/linux_driver/version @@ -1 +1 @@ -1.0 \ No newline at end of file +1.2 \ No newline at end of file diff --git a/source/petalinux_hwdescription/version b/source/petalinux_hwdescription/version index 9f8e9b6..b123147 100644 --- a/source/petalinux_hwdescription/version +++ b/source/petalinux_hwdescription/version @@ -1 +1 @@ -1.0 \ No newline at end of file +1.1 \ No newline at end of file