写这篇博客是年底开始的,我玩了7020(以及7010)已经将近两年了,现在是时候需要总结并分享出来,希望用来开发的朋友之间相互学习、相互交流。首先,我需要说明以下几点:
1.本人QQ: 413069822. 有急切问题可以直接QQ沟通。
2.我是做ARM端开发的,因此下面关于7020的所有内容也基本上是围绕7020 PS端展开的,当然,部分也牵涉到了FPGA 端(比如两者的交互),但是,大部分关于PL端的知识点我是不懂的。
3.我力求以最简单的方法记录最后的结果,因此文字可能较少,这里有不懂的问题可留言,或者直接QQ沟通。
4.所有的问题需要有偿,生活不易,请理解。
要想使用spi,需要在/dev下的spi有相应的节点,但是在直接编译内核时会发现在系统下并没有这样的节点给我们使用,这时候,我们需要手动添加
0 读手册,看引脚
引脚的话由pl端进行约束
set_property PACKAGE_PIN G14 [get_ports SPI0_MISO]
set_property IOSTANDARD LVCMOS33 [get_ports SPI0_MISO]
set_property PACKAGE_PIN J15 [get_ports SPI0_MOSI]
set_property IOSTANDARD LVCMOS33 [get_ports SPI0_MOSI]
set_property PACKAGE_PIN J18 [get_ports SPI0_SCLK]
set_property IOSTANDARD LVCMOS33 [get_ports SPI0_SCLK]
set_property PACKAGE_PIN L19 [get_ports SPI_CS_tri_o[0]]
set_property PACKAGE_PIN M17 [get_ports SPI_CS_tri_o[1]]
set_property PACKAGE_PIN G19 [get_ports SPI_CS_tri_o[2]]
# set_property PACKAGE_PIN J20 [get_ports SPI_CS_tri_o[3]]
# set_property PACKAGE_PIN J19 [get_ports SPI_CS_tri_o[4]]
# set_property PACKAGE_PIN F16 [get_ports SPI_CS_tri_o[5]]
# set_property PACKAGE_PIN F17 [get_ports SPI_CS_tri_o[6]]
set_property IOSTANDARD LVCMOS33 [get_ports SPI_CS_tri_o[*]]
1 重新编译上面已经约束过的的文件,
设备树部分,对于spi配置的描述应该增加以下内容
注:所用到的设备树文件包括
system-top.dts
system-conf.dtsi
pl.dtsi
pcw.dtsi
// 对于 system-top.dts,添加 对spi1节点的引用声明
aliases {
ethernet0 = &gem0;
serial0 = &uart1;
serial1 = &uart0;
spi0 = &qspi;
spi1 = &spi0;
};
// 对于 pcw.dtsi,添加对于 spi1 设备的描述
&spi0 {
is-decoded-cs = <0>;
num-cs = <3>;
status = "okay";
};
// 上面pcw.dtsi添加并修改成
&spi0 {
// 下面这三行不变
is-decoded-cs = <0>;
num-cs = <3>;
status = "okay";
device@0 {
compatible = "spidev";
reg = <0>;
spi-max-frequency = <5000000>;
#address-cells = <1>;
#size-cells = <1>;
};
};
2 修改内核编译配置
make ARCH=arm CROSS_COMPILE=/usr/local/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- menuconfig
#### 执行修改操作
make ARCH=arm CROSS_COMPILE=/usr/local/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- all
## 配置下面两个宏使之生效
CONFIG_SPI_CADENCE = y
CONFIG_SPI_SPIDEV = y
# 并打开以下宏
Device Drivers --->
[*] SPI support --->
<*> Cadence SPI controller
<*> Xilinx SPI controllor common module
<*> User mode SPI device driver support
3 重新编译,烧录,可以看到在 /dev 下有对应的spi设备节点
这个图只是示例而已,我添加了多个节点。
4 测试
spi_mio.h
#ifndef _SPI_MIO_H_
#define _SPI_MIO_H_
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <iostream>
#include <string>
#include <cstring>
#include <fstream>
#include <sstream>
#include <queue>
#include <chrono>
#include <vector>
#include <list>
#include <map>
#include <numeric>
#include <stdexcept>
#include <utility>
#include <functional>
#include <atomic>
#include <mutex>
#include <thread>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/time.h>
#include <sys/times.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <sys/mman.h>
#include <math.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/fb.h>
#include <linux/spi/spidev.h>
#include <termios.h>
#include <sys/vfs.h>
#include <mntent.h>
#include <sys/epoll.h>
#include <poll.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/shm.h>
using namespace std;
class spi
{
private:
std::string device;
uint32_t mode;
uint8_t bits;
uint32_t speed;
public:
int fd_spi;
using ptr = std::shared_ptr<spi>;
public:
// 默认为/dev下的设备节点,可根据实际变化,模式0,一次传输8bits, 速率1e6
spi(std::string device = "/dev/spidev1.0", uint32_t mode = 0, uint8_t bits = 8, uint32_t speed = 1e6);
~spi();
int init();
int transfer(int fd, uint8_t *tx, uint8_t *rx, uint32_t len);
void delay(uint32_t count);
};
class my_dev : public spi
{
private:
uint8_t my_dev_register_config[3] = {0x01, 0x00, 0x34};
public:
using spi::spi;
my_dev() {}
~my_dev() {}
void my_dev_init();
};
#endif
spi_mio.cpp
#include "spi_mio.h"
using namespace std;
spi::spi(std::string device_, uint32_t mode_, uint8_t bits_, uint32_t speed_)
{
device = device_;
mode = mode_;
bits = bits_;
speed = speed_;
fd_spi = open(device_.c_str(), O_RDWR);
if (fd_spi < 0) {
std::cout << "can't open spi device" << std::endl;
}
init();
}
spi::~spi()
{
if (fd_spi > 0) {
close(fd_spi);
}
}
int spi::init()
{
int ret = 0;
if (fd_spi > 0)
{
if (ioctl(fd_spi, SPI_IOC_WR_MODE32, &mode) == -1 ||
ioctl(fd_spi, SPI_IOC_RD_MODE32, &mode) == -1 ||
ioctl(fd_spi, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1 ||
ioctl(fd_spi, SPI_IOC_RD_BITS_PER_WORD, &bits) == -1 ||
ioctl(fd_spi, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1 ||
ioctl(fd_spi, SPI_IOC_RD_MAX_SPEED_HZ, &speed) == -1)
{
std::cout << "spi device init failed" << std::endl;
return -1;
}
}
return ret;
}
int spi::transfer(int fd, uint8_t *tx, uint8_t *rx, uint32_t len)
{
int ret = 0;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = len,
// .speed_hz = speed,
// .bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1){
std::cout << "can't send spi message" << std::endl;
return -1;
}
return ret;
}
void spi::delay(uint32_t count)
{
uint32_t fool = 0;
while (count--){
fool++;
}
}
void my_dev::my_dev_init()
{
uint8_t recv[8];
write(fd_spi, my_dev_register_config, 3);
// transfer(fd_spi, my_dev_register_config, recv, 3); // 用于双向传输
}
main.cpp
#include "spi_mio.h"
int main()
{
my_dev dev1;
dev1.my_dev_init();
return 0;
}
编译&执行
# 这里交叉编译工具链的路径根据实际修改
/usr/local/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ -std=c++11 main.cpp spi_mio.cpp -o main
拷贝到开发版执行即可