feat(pl,drv): 实现了4路相机的独立触发输出
1. 修改了encoder模块的PL端设计和驱动,修改为为4路相机的独立触发输出
2. 修改了启动方式,将比特流文件从BOOT.BIN中分离,实现PL端逻辑的动态加载
BREAKING CHANGE: encoder模块的PL逻辑和linux驱动有变化,驱动的write接收参数从
struct {
unsigned int valve_divide_value;
unsigned int camera_divide_value;
} encoder_dev_divide_value_structure;
变为
struct {
unsigned int valve_divide_value;
unsigned int camera_a_divide_value;
unsigned int camera_b_divide_value;
unsigned int camera_c_divide_value;
unsigned int camera_d_divide_value;
} encoder_dev_divide_value_structure;
@ -1,6 +1,6 @@
|
|||||||
# 下位机
|
# 下位机
|
||||||
|
|
||||||
下位机根据传送带脉冲等触发相机,接收上位机给的数据,按其要求控制阀板。本次下位机采用的硬件是[Microphase](https://www.microphase.cn/)的XME0724CB ZYNQ开发板,具体核心板型号为XME0724-10,到手后记得把各排插针焊一下,如左图:
|
下位机按上位机指令根据传送带脉冲等触发相机,完成刨花板的缺陷分选任务。本次下位机不连接阀板,是由PLC翻板机的机械装置作为执行器,对木板进行分选。采用的硬件是[Microphase](https://www.microphase.cn/)的XME0724CB ZYNQ开发板,具体核心板型号为XME0724-10,到手后记得把各排插针焊一下,如左图:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -9,10 +9,10 @@
|
|||||||
IO扩展版提供了
|
IO扩展版提供了
|
||||||
|
|
||||||
- 1个12V电源输入
|
- 1个12V电源输入
|
||||||
- 3个相机触发,2个为冗余
|
- 4个相机触发
|
||||||
- 1个ZYNQ散热风扇接口
|
- 1个ZYNQ散热风扇接口
|
||||||
- 6个编码器输入,5个为冗余
|
- 6个编码器或IO输入
|
||||||
- 8个阀板接口,2个为冗余
|
- 8个阀板接口
|
||||||
|
|
||||||
接线时,12V电源连接到IO扩展板的电源接口,阀板从左到右应连接在阀板接口1~6上,相机线应连接相机触发接口`TRIG1`和对应的`GND`接口,编码器线应连接在编码器输入接口`E1`和对应的`GND`接口。注意底板不连接任何外部电源。
|
接线时,12V电源连接到IO扩展板的电源接口,阀板从左到右应连接在阀板接口1~6上,相机线应连接相机触发接口`TRIG1`和对应的`GND`接口,编码器线应连接在编码器输入接口`E1`和对应的`GND`接口。注意底板不连接任何外部电源。
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 开发
|
## 开发
|
||||||
|
|
||||||
本次开发基于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/)。
|
本次开发基于zynq xc7z010-1clg400芯片,因此FPGA设计软件为Vitis中包含的[Vivado 2022.1](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/vitis.html),Linux编译工具为[petalinux 2022.2](https://china.xilinx.com/support/download/index.html/content/xilinx/zh/downloadNav/embedded-design-tools.html),Linux应用程序编译工具为linaro的[arm-linux-gnueabihf-gcc 12.2.1](https://snapshots.linaro.org/gnu-toolchain/12.2-2023.04-1/)。
|
||||||
|
|
||||||
### 生成硬件描述文件
|
### 生成硬件描述文件
|
||||||
|
|
||||||
@ -10,23 +10,14 @@
|
|||||||
|
|
||||||
### 创建PETALINUX工程
|
### 创建PETALINUX工程
|
||||||
|
|
||||||
1. 创建名为`ps-linux`的工程,并创建两个模块
|
1. 创建名为`ps-linux`的工程
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ cd ~
|
$ cd ~
|
||||||
$ petalinux-create -t project --template zynq -n ps-linux
|
$ petalinux-create -t project --template zynq -n ps-linux
|
||||||
$ petalinux-create -t modules --name fifo --enable
|
|
||||||
$ 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/petalinux_hwdescription/system_wrapper.xsa](source/petalinux_hwdescription/system_wrapper.xsa)到`ps-linux`目录中并config
|
||||||
|
|
||||||
```shell
|
|
||||||
~/ps-linux/project-spec/meta-user/recipes-modules/fifo/files
|
|
||||||
~/ps-linux/project-spec/meta-user/recipes-modules/encoder/files
|
|
||||||
```
|
|
||||||
|
|
||||||
3. 上传硬件描述文件[source/petalinux_hwdescription/system_wrapper.xsa](source/petalinux_hwdescription/system_wrapper.xsa)到`ps-linux`目录中并config
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
system_wrapper.xsa上传到~/ps-linux
|
system_wrapper.xsa上传到~/ps-linux
|
||||||
@ -62,9 +53,21 @@
|
|||||||
# └─Copy final images to tftpboot (不选)
|
# └─Copy final images to tftpboot (不选)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
4. 创建两个模块
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ petalinux-create -t modules --name fifo --enable
|
||||||
|
$ petalinux-create -t modules --name encoder --enable
|
||||||
|
```
|
||||||
|
|
||||||
4. 修改设备树,需要修改的文件为`project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi`,先删除该文件,然后上传新的自定义设备树文件[source/petalinux_devicetree/system-user.dtsi](../source/petalinux_devicetree/system-user.dtsi)
|
1. 分别上传驱动代码[source/linux_driver/fifo.c](../source/linux_driver/fifo.c)和[source/linux_driver/encoder.c](../source/linux_driver/encoder.c)到下面的目录中
|
||||||
|
|
||||||
|
```shell
|
||||||
|
~/ps-linux/project-spec/meta-user/recipes-modules/fifo/files
|
||||||
|
~/ps-linux/project-spec/meta-user/recipes-modules/encoder/files
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 修改设备树,需要修改的文件为`project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi`,先删除该文件,然后上传新的自定义设备树文件[source/petalinux_devicetree/system-user.dtsi](../source/petalinux_devicetree/system-user.dtsi)
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ cd ~/ps-linux/project-spec/meta-user/recipes-bsp/device-tree/files
|
$ cd ~/ps-linux/project-spec/meta-user/recipes-bsp/device-tree/files
|
||||||
@ -72,7 +75,7 @@
|
|||||||
上传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)配置
|
2. 配置kernel,使用命令`petalinux-config -c kernel`,按下面提示或[source/petalinux_config/kernel.cfg](../source/petalinux_config/kernel.cfg)配置
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# File systems
|
# File systems
|
||||||
@ -87,7 +90,7 @@
|
|||||||
# └─OTG support (勾选为星号)
|
# └─OTG support (勾选为星号)
|
||||||
```
|
```
|
||||||
|
|
||||||
6. 配置rootfs,使用命令`petalinux-config -c rootfs`,按下面提示或[source/petalinux_config/rootfs_config](../source/petalinux_config/rootfs_config)配置
|
3. 配置rootfs,使用命令`petalinux-config -c rootfs`,按下面提示或[source/petalinux_config/rootfs_config](../source/petalinux_config/rootfs_config)配置
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# Filesystem Packages
|
# Filesystem Packages
|
||||||
@ -194,6 +197,8 @@
|
|||||||
# └─ADD_USERS_TO_SUDOERS (petalinux)
|
# └─ADD_USERS_TO_SUDOERS (petalinux)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
8. 替换`~/ps-linux/project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h`为[platform-top.h](./petalinux_config/platform-top.h),用于添加u-boot所需的环境变量,实现动态加载比特流文件
|
||||||
|
|
||||||
### 编译系统
|
### 编译系统
|
||||||
|
|
||||||
1. 编译工程,使用命令`petalinux-build`。编译完成,在当前工程目录下生成images文件夹,该命令将生成设备树文件、FSBL文件、U-Boot文件,Linux Kernel文件和rootfs文件镜像
|
1. 编译工程,使用命令`petalinux-build`。编译完成,在当前工程目录下生成images文件夹,该命令将生成设备树文件、FSBL文件、U-Boot文件,Linux Kernel文件和rootfs文件镜像
|
||||||
@ -202,7 +207,7 @@
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ cd ~/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
|
$ petalinux-package --boot --fsbl ./zynq_fsbl.elf --u-boot ./u-boot.elf --force
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -215,14 +220,14 @@ $ petalinux-build -c fifo
|
|||||||
$ petalinux-build -c encoder
|
$ 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`
|
编译后的模块文件为` ps-linux/build/tmp/sysroots-components/zynq_generic/fifo/lib/modules/5.15.36-xilinx-v2022.2/extra/fifo.ko`和`ps-linux/build/tmp/sysroots-components/zynq_generic/encoder/lib/modules/5.15.36-xilinx-v2022.2/extra/encoder.ko`
|
||||||
|
|
||||||
### 编译应用程序
|
### 编译应用程序
|
||||||
|
|
||||||
在运行make时要设置好交叉编译工具链前缀,命令如下
|
在运行make时要设置好交叉编译工具链前缀,命令如下
|
||||||
```shell
|
```shell
|
||||||
$ make CROSS_COMPILE=交叉编译工具链前缀
|
$ 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-
|
例如 make CROSS_COMPILE=/home/miaow/software/gcc-linaro-12.2.1-2023.04-x86_64_arm-linux-gnueabihf/bin/arm-none-linux-gnueabihf-
|
||||||
```
|
```
|
||||||
|
|
||||||
编译后的可执行文件为工程目录的`build/target`,交叉编译工具链前缀也可以在Makefile中修改设定
|
编译后的可执行文件为工程目录的`build/target`,交叉编译工具链前缀也可以在Makefile中修改设定
|
||||||
@ -245,7 +250,9 @@ $ make CROSS_COMPILE=交叉编译工具链前缀
|
|||||||
| 2048~x扇区 | 100M | C W95 FAT32 (LBA) | FAT32 | boot |
|
| 2048~x扇区 | 100M | C W95 FAT32 (LBA) | FAT32 | boot |
|
||||||
| x扇区~最后扇区 | ≈SD卡大小-100M | 83 Linux | ext4 | rootfs |
|
| x扇区~最后扇区 | ≈SD卡大小-100M | 83 Linux | ext4 | rootfs |
|
||||||
|
|
||||||
2. 将打包和编译得到的BOOT.BIN、boot.scr和image.ub复制到boot分区;将rootfs.tar.gz解压到rootfs分区。
|
2. 将打包和编译得到的BOOT.BIN、boot.scr、system.bit和image.ub复制到boot分区;将rootfs.tar.gz解压到rootfs分区
|
||||||
|
|
||||||
|
> 注意: 这里的system.bit为比特流文件,可以由petalinux从XSA文件中提取,也可以是vivado生成的,注意命名为system.bit。
|
||||||
|
|
||||||
3. 拨码开关拨到SD卡启动,插入SD卡到XME0724底板上,上电启动。
|
3. 拨码开关拨到SD卡启动,插入SD卡到XME0724底板上,上电启动。
|
||||||
|
|
||||||
@ -279,8 +286,6 @@ $ make CROSS_COMPILE=交叉编译工具链前缀
|
|||||||
$ reboot
|
$ reboot
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
7. 电脑网卡设置到开发板同一网段 SSH连接信息如下
|
7. 电脑网卡设置到开发板同一网段 SSH连接信息如下
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 539 KiB |
@ -1,12 +1,12 @@
|
|||||||
# 硬件平台
|
# 硬件平台
|
||||||
|
|
||||||
PL端主要由4个外设组成,分别时**风扇控制器**(FAN),**编码和分频控制器**(ENCODER),**先入先出队列*(FIFO),阀板控制器**(VALVE)。其中阀板控制器没有提供AXI接口,因此并没有映射寄存器,软件也无法进行控制。各个控制器的连接关系如下图所示。
|
PL端主要由4个外设组成,分别时**风扇控制器**(FAN),**编码和分频控制器**(ENCODER),**先入先出队列**(FIFO),**阀板控制器**(VALVE)。其中阀板控制器没有提供AXI接口,因此并没有映射寄存器,软件也无法进行控制。各个控制器的连接关系如下图所示。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
由于开发板的PL端没有自带晶振,所以4个外设由统一的同步时钟驱动,时钟源来自PS端,为200MHz,软件不可修改。外部编码器信号输入**编码和分频控制器**,控制器根据软件设置的阀触发分频值和相机触发分频值对编码器信号进行分频,分频后的信号用于驱动喷阀动作和触发相机拍照。光谱相机的触发频率较慢,为编码器频率/CDIV;彩色相机触发频率快,为⌊编码器频率/CDIV⌋\* 4。上位机发来的喷阀动作数据有两个,对应的分别是光谱和彩色相机的识别结果,都是为256宽度*1024高度。识别结果存储到**先入先出队列**中。
|
由于开发板的PL端没有自带晶振,所以4个外设由统一的同步时钟驱动,时钟源来自PS端,为200MHz,软件不可修改。外部编码器信号输入**编码和分频控制器**,控制器根据软件设置的阀触发分频值和相机触发分频值对编码器信号进行分频,分频后的信号用于驱动喷阀动作和触发相机拍照。上位机的识别结果存储到**先入先出队列**中。
|
||||||
|
|
||||||
为同步触发相机和队列移出信号以及保持队列中数据的动态平衡,两个先入先出队列在彩色相机触发同时输出一个数据,即**先入先出队列**的读信号和彩色相机触发共用同一个信号。而由于电磁阀的物理特性导致电磁阀可能无法以触发相机的频率进行开关,因此**阀板控制器**对先入先出队列输出总线上的数据重采样,即按照**编码和分频控制器**输出的阀触发信号读入。两个先入先出队列移出的数据在**阀板控制器**中进行按位或运算,得到的数据转换为阀板协议,输出给阀板。**风扇控制器**用于驱动风扇的启停,给ZYNQ芯片进行降温,防止芯片过热导致工作中出现问题。由于风扇寿命短,因此目前采用散热片方案,风扇不开。
|
为同步触发相机和队列移出信号以及保持队列中数据的动态平衡,两个先入先出队列在相机触发同时输出一个数据,即**先入先出队列**的读信号和相机触发共用同一个信号。而由于电磁阀的物理特性导致电磁阀可能无法以触发相机的频率进行开关,因此**阀板控制器**对先入先出队列输出总线上的数据重采样,即按照**编码和分频控制器**输出的阀触发信号读入。两个先入先出队列移出的数据在**阀板控制器**中进行按位或运算,得到的数据转换为阀板协议,输出给阀板。**风扇控制器**用于驱动风扇的启停,给ZYNQ芯片进行降温,防止芯片过热导致工作中出现问题。由于风扇寿命短,因此目前采用散热片方案,风扇不开。
|
||||||
|
|
||||||
## PS模块
|
## PS模块
|
||||||
|
|
||||||
@ -31,18 +31,13 @@ ARM核上还开启AXI GP0接口与PL通信
|
|||||||
|
|
||||||
## ENCODER模块
|
## ENCODER模块
|
||||||
|
|
||||||
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
|
encoder模块主要接口为in_signal、out_signal_camera_a_posedge、out_signal_camera_b_posedge、out_signal_camera_c_posedge、out_signal_camera_d_posedge、out_signal_valve_posedge、out_signal_camera_a、out_signal_camera_b、out_signal_camera_c、out_signal_camera_d、out_signal_valve
|
||||||
|
|
||||||
接口比较乱,这个模块内部逻辑也实现的不优雅,这得怪老倪动不动就改需求
|
终于,我们重新实现了被老倪乱起八糟的需求搞的几乎奔溃的模块
|
||||||
|
|
||||||
1. in_signal接口与外部编码器相连,接收外部编码器信号
|
1. in_signal接口与外部编码器相连,接收外部编码器信号
|
||||||
|
2. out_signal_camera_a到d最多课用于触发共4个相机
|
||||||
2. out_signal_camera用于触发光谱相机拍照,可以认为光谱相机一次触发产生4行喷阀数据
|
3. out_signal_camera_posedge_a到d为上述信号的上升沿,其中out_signal_camera_posedge_a控制FIFO的读出
|
||||||
|
|
||||||
3. out_signal_camera_x4则为out_signal_camera的4倍频率,触发彩色相机,彩色相机一次产生1行喷阀数据
|
|
||||||
|
|
||||||
4. out_signal_camera_posedge_x4为out_signal_camera_x4的上升沿,控制FIFO的读出
|
|
||||||
|
|
||||||
5. out_signal_valve_posedge为out_signal_valve的上升沿,驱动**阀板控制器**动作
|
5. out_signal_valve_posedge为out_signal_valve的上升沿,驱动**阀板控制器**动作
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
1
doc/pl_reference_mannual.assets/encoder_cdivrx.svg
Normal file
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
1
doc/pl_reference_mannual.assets/encoder_cr.svg
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 25 KiB |
1
doc/pl_reference_mannual.assets/encoder_regs.svg
Normal file
|
After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
1
doc/pl_reference_mannual.assets/encoder_vdivr.svg
Normal file
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 539 KiB |
@ -100,7 +100,7 @@ ENCODER模块的寄存器主要有控制寄存器 (ENCODER_CR)、阀触发分频
|
|||||||
|
|
||||||
偏移地址: 0x00<br/>复位值: 0x0000 0000
|
偏移地址: 0x00<br/>复位值: 0x0000 0000
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
| **Field** | **Description** |
|
| **Field** | **Description** |
|
||||||
| :---------- | :----------------------------------------------------------- |
|
| :---------- | :----------------------------------------------------------- |
|
||||||
@ -113,17 +113,19 @@ ENCODER模块的寄存器主要有控制寄存器 (ENCODER_CR)、阀触发分频
|
|||||||
|
|
||||||
偏移地址: 0x04<br/>复位值: 0x0000 0000
|
偏移地址: 0x04<br/>复位值: 0x0000 0000
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
| **Field** | **Description** |
|
| **Field** | **Description** |
|
||||||
| :-------------- | :----------------------------------------------------------- |
|
| :-------------- | :----------------------------------------------------------- |
|
||||||
| 位31:0 **VDIV** | 阀触发分频值<br /> 阀控制器重采样频率和编码器脉冲的分频值, 写入数据后编码和分频控制器自动清除缓存并应用新的数值 <br /> 注意:0表示不间断触发,即PL端每个时钟周期均触发阀模块 |
|
| 位31:0 **VDIV** | 阀触发分频值<br /> 阀控制器重采样频率和编码器脉冲的分频值, 写入数据后编码和分频控制器自动清除缓存并应用新的数值 <br /> 注意:0表示不间断触发,即PL端每个时钟周期均触发阀模块 |
|
||||||
|
|
||||||
#### ENCODER相机触发分频寄存器 (ENCODER_CDIVR)
|
#### ENCODER相机触发分频寄存器 (ENCODER_CDIVRx) (x=A...D)
|
||||||
|
|
||||||
|
用于设定相机A到相机D的分频系数,各相机可独立设置
|
||||||
|
|
||||||
偏移地址: 0x08<br/>复位值: 0x0000 0000
|
偏移地址: 0x08<br/>复位值: 0x0000 0000
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
| **Field** | **Description** |
|
| **Field** | **Description** |
|
||||||
| :-------------- | :----------------------------------------------------------- |
|
| :-------------- | :----------------------------------------------------------- |
|
||||||
@ -133,7 +135,7 @@ ENCODER模块的寄存器主要有控制寄存器 (ENCODER_CR)、阀触发分频
|
|||||||
|
|
||||||
ENCODER寄存器可映射为32位可寻址寄存器,如下表所述:
|
ENCODER寄存器可映射为32位可寻址寄存器,如下表所述:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 先入先出队列 (FIFO)
|
## 先入先出队列 (FIFO)
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
1.2
|
1.3
|
||||||
@ -9,8 +9,8 @@
|
|||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <linux/cdev.h>
|
#include <linux/cdev.h>
|
||||||
|
|
||||||
#define ENCODER_CNT 1 /* 主设备号 */
|
#define ENCODER_CNT 1
|
||||||
#define ENCODER_NAME "encoder" /* 设备名字 */
|
#define ENCODER_NAME "encoder"
|
||||||
|
|
||||||
#define ENCODER_CMD_FUNCTION_CLEAR 1
|
#define ENCODER_CMD_FUNCTION_CLEAR 1
|
||||||
#define ENCODER_CMD_FUNCTION_VIRT_INPUT 2
|
#define ENCODER_CMD_FUNCTION_VIRT_INPUT 2
|
||||||
@ -26,31 +26,39 @@
|
|||||||
#define ENCODER_REG_1_OFFSET 0x00000004
|
#define ENCODER_REG_1_OFFSET 0x00000004
|
||||||
#define ENCODER_REG_2_OFFSET 0x00000008
|
#define ENCODER_REG_2_OFFSET 0x00000008
|
||||||
#define ENCODER_REG_3_OFFSET 0x0000000C
|
#define ENCODER_REG_3_OFFSET 0x0000000C
|
||||||
|
#define ENCODER_REG_4_OFFSET 0x00000010
|
||||||
|
#define ENCODER_REG_5_OFFSET 0x00000014
|
||||||
|
#define ENCODER_REG_6_OFFSET 0x00000018
|
||||||
|
#define ENCODER_REG_7_OFFSET 0x0000001C
|
||||||
|
|
||||||
#define ENCODER_CR_VTS_MASK ((u32)(1 << 2)) // 内部触发信号 (Virtual Triggle Signal) MOD位置1时,由上位机软件写入,将该位信号作为触发信号
|
#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_MOD_MASK ((u32)(1 << 1)) // 模式选择 (Mode) 0: 外部触发模式,外部触发编码器转动, 1: 内部触发模式,上位机软件模拟触发信号
|
||||||
#define ENCODER_CR_CLR_MASK ((u32)(1 << 0)) // 清除缓存 (Clear) 清除编码和分频控制器内部的分频计数值,不影响VDIV和CDIV
|
#define ENCODER_CR_CLR_MASK ((u32)(1 << 0)) // 清除缓存 (Clear) 清除编码和分频控制器内部的分频计数值,不影响VDIV和CDIV
|
||||||
|
|
||||||
/* 映射后的寄存器虚拟地址指针 */
|
|
||||||
static void __iomem *encoder_cr_addr;
|
static void __iomem *encoder_cr_addr;
|
||||||
static void __iomem *encoder_vdivr_addr;
|
static void __iomem *encoder_vdivr_addr;
|
||||||
static void __iomem *encoder_cdivr_addr;
|
static void __iomem *encoder_cdivra_addr;
|
||||||
static void __iomem *encoder_reg3_addr;
|
static void __iomem *encoder_cdivrb_addr;
|
||||||
|
static void __iomem *encoder_cdivrc_addr;
|
||||||
|
static void __iomem *encoder_cdivrd_addr;
|
||||||
|
|
||||||
struct encoder_dev
|
struct encoder_dev
|
||||||
{
|
{
|
||||||
dev_t devid; /* 设备号 */
|
dev_t devid;
|
||||||
struct cdev cdev; /* cdev */
|
struct cdev cdev;
|
||||||
struct class *class; /* 类 */
|
struct class *class;
|
||||||
struct device *device; /* 设备 */
|
struct device *device;
|
||||||
int major; /* 主设备号 */
|
int major;
|
||||||
int minor; /* 次设备号 */
|
int minor;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
u32 valve_divide_value;
|
u32 valve_divide_value;
|
||||||
u32 camera_divide_value;
|
u32 camera_a_divide_value;
|
||||||
|
u32 camera_b_divide_value;
|
||||||
|
u32 camera_c_divide_value;
|
||||||
|
u32 camera_d_divide_value;
|
||||||
} kernelbuf_typedef;
|
} kernelbuf_typedef;
|
||||||
|
|
||||||
static struct encoder_dev encoder;
|
static struct encoder_dev encoder;
|
||||||
@ -82,7 +90,10 @@ static ssize_t encoder_write(struct file *filp, const char __user *buf, size_t c
|
|||||||
u32 data;
|
u32 data;
|
||||||
kernelbuf_typedef kern_buf = {
|
kernelbuf_typedef kern_buf = {
|
||||||
.valve_divide_value = 0,
|
.valve_divide_value = 0,
|
||||||
.camera_divide_value = 0,
|
.camera_a_divide_value = 0,
|
||||||
|
.camera_b_divide_value = 0,
|
||||||
|
.camera_c_divide_value = 0,
|
||||||
|
.camera_d_divide_value = 0,
|
||||||
};
|
};
|
||||||
if (cnt != sizeof(kern_buf))
|
if (cnt != sizeof(kern_buf))
|
||||||
{
|
{
|
||||||
@ -95,20 +106,22 @@ static ssize_t encoder_write(struct file *filp, const char __user *buf, size_t c
|
|||||||
printk(KERN_ERR "kernel write failed!\r\n");
|
printk(KERN_ERR "kernel write failed!\r\n");
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
// 最小分频值为4
|
// 最小分频值为2
|
||||||
if (kern_buf.valve_divide_value < 2 || kern_buf.camera_divide_value < 4)
|
if (kern_buf.valve_divide_value < 2 || kern_buf.camera_a_divide_value < 2 ||
|
||||||
{
|
kern_buf.camera_b_divide_value < 2 || kern_buf.camera_c_divide_value < 2 ||
|
||||||
|
kern_buf.camera_d_divide_value < 2)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
|
||||||
|
|
||||||
// 写入0后清除ENCODER内部计数器缓存清除
|
// 写入0后清除ENCODER内部计数器缓存清除
|
||||||
data = readl(encoder_cr_addr);
|
data = readl(encoder_cr_addr);
|
||||||
writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr);
|
writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr);
|
||||||
|
|
||||||
if (kern_buf.valve_divide_value != 0)
|
|
||||||
writel(kern_buf.valve_divide_value, encoder_vdivr_addr);
|
writel(kern_buf.valve_divide_value, encoder_vdivr_addr);
|
||||||
if (kern_buf.camera_divide_value != 0)
|
writel(kern_buf.camera_a_divide_value, encoder_cdivra_addr);
|
||||||
writel(kern_buf.camera_divide_value, encoder_cdivr_addr);
|
writel(kern_buf.camera_b_divide_value, encoder_cdivrb_addr);
|
||||||
|
writel(kern_buf.camera_c_divide_value, encoder_cdivrc_addr);
|
||||||
|
writel(kern_buf.camera_d_divide_value, encoder_cdivrd_addr);
|
||||||
|
|
||||||
// 写入1退出清除状态,使得ENCODER内部计数器能正常工作,内部计数器在正常工作前已经被清零
|
// 写入1退出清除状态,使得ENCODER内部计数器能正常工作,内部计数器在正常工作前已经被清零
|
||||||
writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr);
|
writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr);
|
||||||
@ -172,7 +185,7 @@ static long encoder_ioctl(struct file *fp, unsigned int cmd, unsigned long tmp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 设备操作函数 */
|
// 设备操作函数
|
||||||
static struct file_operations encoder_fops = {
|
static struct file_operations encoder_fops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.open = encoder_open,
|
.open = encoder_open,
|
||||||
@ -185,14 +198,16 @@ static int __init encoder_init(void)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
u32 data;
|
u32 data;
|
||||||
/* 寄存器地址映射 */
|
// 寄存器地址映射
|
||||||
encoder_cr_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_0_OFFSET, 4);
|
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_vdivr_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_1_OFFSET, 4);
|
||||||
encoder_cdivr_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_2_OFFSET, 4);
|
encoder_cdivra_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_2_OFFSET, 4);
|
||||||
encoder_reg3_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_3_OFFSET, 4);
|
encoder_cdivrb_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_3_OFFSET, 4);
|
||||||
|
encoder_cdivrc_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_4_OFFSET, 4);
|
||||||
|
encoder_cdivrd_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_5_OFFSET, 4);
|
||||||
|
|
||||||
/* 注册字符设备驱动 */
|
|
||||||
//(1)创建设备号
|
// 创建设备号
|
||||||
if (encoder.major)
|
if (encoder.major)
|
||||||
{
|
{
|
||||||
encoder.devid = MKDEV(encoder.major, 0);
|
encoder.devid = MKDEV(encoder.major, 0);
|
||||||
@ -209,16 +224,16 @@ static int __init encoder_init(void)
|
|||||||
encoder.minor = MINOR(encoder.devid);
|
encoder.minor = MINOR(encoder.devid);
|
||||||
}
|
}
|
||||||
|
|
||||||
//(2)初始化cdev
|
// 初始化cdev
|
||||||
encoder.cdev.owner = THIS_MODULE;
|
encoder.cdev.owner = THIS_MODULE;
|
||||||
cdev_init(&encoder.cdev, &encoder_fops);
|
cdev_init(&encoder.cdev, &encoder_fops);
|
||||||
|
|
||||||
//(3)添加cdev
|
// 添加cdev
|
||||||
ret = cdev_add(&encoder.cdev, encoder.devid, ENCODER_CNT);
|
ret = cdev_add(&encoder.cdev, encoder.devid, ENCODER_CNT);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto FAIL_ADD_CDEV;
|
goto FAIL_ADD_CDEV;
|
||||||
|
|
||||||
//(4)创建类
|
// 创建类
|
||||||
encoder.class = class_create(THIS_MODULE, ENCODER_NAME);
|
encoder.class = class_create(THIS_MODULE, ENCODER_NAME);
|
||||||
if (IS_ERR(encoder.class))
|
if (IS_ERR(encoder.class))
|
||||||
{
|
{
|
||||||
@ -226,7 +241,7 @@ static int __init encoder_init(void)
|
|||||||
goto FAIL_CREATE_CLASS;
|
goto FAIL_CREATE_CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
//(5)创建设备
|
// 创建设备
|
||||||
encoder.device = device_create(encoder.class, NULL, encoder.devid, NULL, ENCODER_NAME);
|
encoder.device = device_create(encoder.class, NULL, encoder.devid, NULL, ENCODER_NAME);
|
||||||
if (IS_ERR(encoder.device))
|
if (IS_ERR(encoder.device))
|
||||||
{
|
{
|
||||||
@ -234,13 +249,15 @@ static int __init encoder_init(void)
|
|||||||
goto FAIL_CREATE_DEV;
|
goto FAIL_CREATE_DEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
//默认分频系数1000
|
// 默认分频系数1000
|
||||||
data = readl(encoder_cr_addr);
|
data = readl(encoder_cr_addr);
|
||||||
writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr); // 清除硬件计数器缓存
|
writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr); // 清除硬件计数器缓存
|
||||||
writel(1000, encoder_vdivr_addr); // 设置阀触发分频
|
writel(1000, encoder_vdivr_addr); // 设置阀触发分频
|
||||||
writel(1000, encoder_cdivr_addr); // 设置相机触发分频
|
writel(1000, encoder_cdivra_addr); // 设置相机a触发分频
|
||||||
|
writel(1000, encoder_cdivrb_addr); // 设置相机b触发分频
|
||||||
|
writel(1000, encoder_cdivrc_addr); // 设置相机c触发分频
|
||||||
|
writel(1000, encoder_cdivrd_addr); // 设置相机d触发分频
|
||||||
writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr); // 清除完毕
|
writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr); // 清除完毕
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
FAIL_CREATE_DEV:
|
FAIL_CREATE_DEV:
|
||||||
@ -255,34 +272,37 @@ FAIL_ADD_CDEV:
|
|||||||
FAIL_REGISTER_CHR_DEV:
|
FAIL_REGISTER_CHR_DEV:
|
||||||
iounmap(encoder_cr_addr);
|
iounmap(encoder_cr_addr);
|
||||||
iounmap(encoder_vdivr_addr);
|
iounmap(encoder_vdivr_addr);
|
||||||
iounmap(encoder_cdivr_addr);
|
iounmap(encoder_cdivra_addr);
|
||||||
iounmap(encoder_reg3_addr);
|
iounmap(encoder_cdivrb_addr);
|
||||||
|
iounmap(encoder_cdivrc_addr);
|
||||||
|
iounmap(encoder_cdivrd_addr);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit encoder_exit(void)
|
static void __exit encoder_exit(void)
|
||||||
{
|
{
|
||||||
//(1)注销设备
|
// 注销设备
|
||||||
device_destroy(encoder.class, encoder.devid);
|
device_destroy(encoder.class, encoder.devid);
|
||||||
|
|
||||||
//(2)注销类
|
// 注销类
|
||||||
class_destroy(encoder.class);
|
class_destroy(encoder.class);
|
||||||
|
|
||||||
//(3)删除cdev
|
// 删除cdev
|
||||||
cdev_del(&encoder.cdev);
|
cdev_del(&encoder.cdev);
|
||||||
|
|
||||||
//(4)注销设备号
|
// 注销设备号
|
||||||
unregister_chrdev_region(encoder.devid, ENCODER_CNT);
|
unregister_chrdev_region(encoder.devid, ENCODER_CNT);
|
||||||
|
|
||||||
//(5)取消内存映射
|
// 取消内存映射
|
||||||
iounmap(encoder_cr_addr);
|
iounmap(encoder_cr_addr);
|
||||||
iounmap(encoder_vdivr_addr);
|
iounmap(encoder_vdivr_addr);
|
||||||
iounmap(encoder_cdivr_addr);
|
iounmap(encoder_cdivra_addr);
|
||||||
iounmap(encoder_reg3_addr);
|
iounmap(encoder_cdivrb_addr);
|
||||||
|
iounmap(encoder_cdivrc_addr);
|
||||||
|
iounmap(encoder_cdivrd_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 驱动模块入口和出口函数注册 */
|
|
||||||
module_init(encoder_init);
|
module_init(encoder_init);
|
||||||
module_exit(encoder_exit);
|
module_exit(encoder_exit);
|
||||||
MODULE_AUTHOR("DingKun");
|
MODULE_AUTHOR("DingKun");
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
1.2
|
1.3
|
||||||
10
source/petalinux_config/platform-top.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include <configs/zynq-common.h>
|
||||||
|
|
||||||
|
#define CONFIG_BOOTCOMMAND "run mmc_loadbit; run distro_bootcmd"
|
||||||
|
#define CONFIG_FPGA_ZYNQPL
|
||||||
|
#define CONFIG_EXTRA_ENV_SETTINGS \
|
||||||
|
BOOTENV \
|
||||||
|
"bitstream_bit=system.bit\0" \
|
||||||
|
"bitstream=system.bit\0" \
|
||||||
|
"loadbit_addr=0x100000\0" \
|
||||||
|
"mmc_loadbit=echo Loading bitstream from SD/MMC/eMMC to RAM.. && mmcinfo && load mmc 0 ${loadbit_addr} ${bitstream} && fpga loadb 0 ${loadbit_addr} ${filesize}\0"
|
||||||
@ -1 +1 @@
|
|||||||
1.0
|
1.1
|
||||||