OMAPL138经过SPI与fpga通信

【1】FPGA端
【1.1】顶层模块分为SPI接口、寄存器组、PLL这三个部分。node

 1 module top(
 2     input        clkin,
 3 
 4     // spi
 5     output        arm_spi_miso,
 6     input        arm_spi_mosi,
 7     input        arm_spi_cs,
 8     input        arm_spi_clk
 9 );
10 
11     reg [15:0] reg_sum = 12;
12 
13     // reg_array
14     wire [15:0]    status_sum;
15     wire [15:0]    attr_sum;
16     wire [15:0]    attr_fibre_len;
17 
18     // test
19     assign status_sum = reg_sum;
20 
21     // reg_array 和 spi_slave 之间的接线
22     wire [6:0]    m_addr;
23     wire [15:0]    m_din;
24     wire [15:0]    m_dout;
25     wire        m_we;
26     wire        m_latch;
27 
28     //
29     wire sys_clk;
30 
31     reg_array reg_array_ins(
32         // to spi_slave
33         .addr(m_addr),
34         .we(m_we),
35         .din(m_din),
36         .dout(m_dout),
37         .latch(m_latch),
38 
39         // wire
40         .status_sum,
41 
42         .attr_sum,
43         .attr_fibre_len
44     );
45 
46     spi_slave spi_slave_ins(
47         // pin
48         .spi_cs(arm_spi_cs),
49         .spi_clk(arm_spi_clk),
50         .spi_mosi(arm_spi_mosi),
51         .spi_miso(arm_spi_miso),
52 
53         // sysclk
54         .sys_clk(sys_clk),
55 
56         // to reg_array
57         .m_addr(m_addr),
58         .m_din(m_dout),
59         .m_dout(m_din),
60         .m_we(m_we),
61         .m_latch(m_latch)
62     );
63     pll0 pll0_ins(
64         .CLK_IN1(clkin),
65         .CLK_OUT1(sys_clk)
66     );
67 endmodule
top.v

【1.2】SPI接口部分,在SPI_CLK的上升沿锁存输入,降低沿移位输出,帧结构为7bit addr+1bit we+16bit data。经过晶振时钟来检测CS的两个边沿,置msg_begin标记,并在spi_clk的第一个上升沿初始化计数器。
若是spi_clk不是从GLCK脚输入的,则也能够用晶振时钟或PLL输出来检测spi_clk的上升和降低沿,固然spi_clk最高速率通常不能超过检测时钟频率的1/4。linux

  1 module spi_slave(
  2     input        spi_cs,
  3     input        spi_clk,
  4     input        spi_mosi,
  5     output        spi_miso,
  6 
  7     input        sys_clk,
  8 
  9     output   [6:0]    m_addr,
 10     input    [15:0]   m_din,
 11     output   [15:0]   m_dout,
 12     output          m_we,            // 1=write
 13     output          m_latch        // read/write at the rasing edge
 14 );
 15     reg [4:0]    shift_count    = 24;
 16     reg [15:0]    reg_data_in    = 0;        // 从mosi接收的数据
 17     reg [15:0]    reg_data_out    = 0;        // 发送往miso的数据
 18 
 19     reg      reg_we        = 0;
 20     assign    m_we        = reg_we;
 21 
 22     assign    spi_miso    = reg_data_out[15];
 23     assign    m_addr        = reg_data_in[6:0];
 24     assign    m_dout        = reg_data_in;        // 送往reg_array
 25     reg      reg_latch    = 1;
 26 
 27     // CS边沿检测
 28     reg msg_begin     = 0;
 29     reg [2:0] reg_cs = 3'b111;
 30     always @(posedge sys_clk)
 31     begin
 32         reg_cs <= {reg_cs[1:0], spi_cs};
 33 
 34         if(reg_cs[2:1] == 2'b10)
 35         begin
 36             msg_begin    <= 1;
 37         end
 38 
 39         if(reg_cs[2:1] == 2'b00 && shift_count != 24)
 40         begin
 41             msg_begin    <= 0;
 42         end
 43 
 44         if(reg_cs[2:1] == 2'b01)
 45         begin
 46             msg_begin    <= 1;
 47         end
 48     end
 49 
 50     always @(posedge spi_clk)
 51     begin
 52         if(spi_cs == 0)
 53         begin
 54             if(msg_begin == 1)
 55             begin
 56                 shift_count    <= 1;            // 初始化shift_count
 57             end else begin
 58                 if(shift_count < 24)
 59                 begin
 60                     shift_count    <= shift_count + 1'b1;
 61                 end
 62             end
 63 
 64             reg_data_in <= {reg_data_in[14:0], spi_mosi};    // 地址在第7个降低沿生效,
 65         end
 66     end
 67 
 68     always @(negedge spi_clk)
 69     begin
 70         if(spi_cs == 0)
 71         begin
 72             if(shift_count == 1)                // 拉低latch
 73             begin
 74                 reg_latch    <= 0;
 75                 reg_we        <= 0;            // 默认读取
 76             end
 77             if(shift_count == 7)                // 地址已经在第7个上升沿锁存完成,让reg_array准备数据
 78             begin
 79                 reg_latch    <= 1;
 80             end
 81             if(shift_count == 8)                // 拉低latch
 82             begin
 83                 reg_latch    <= 0;
 84             end
 85 
 86             // 准备读取数据
 87             if(shift_count == 8)                // 写入时也输出
 88             begin
 89                 reg_data_out    <= m_din;
 90                 reg_we        <= reg_data_in[0];    // 锁存读写位备用
 91             end
 92             // 移位输出
 93             if(shift_count > 8)                // 写入时也输出
 94             begin
 95                 reg_data_out <= {reg_data_out[14:0], 1'b0};
 96             end
 97 
 98             // 处理写入
 99             if(shift_count == 24 && reg_we == 1)
100             begin
101                 reg_latch    <= 1;
102             end
103         end
104     end
105     assign m_latch = reg_latch;
106 
107 endmodule
spi_slave.v

【1.3】寄存器组则是对SPI来的地址译码和读写。在latch的上升沿,WE=1写入数据,WE=0读取数据。ide

 1 module reg_array(
 2     input    [6:0]    addr,
 3     input        we,
 4     input    [15:0]    din,
 5     output    reg [15:0]    dout,
 6     input        latch,
 7 
 8     input    [15:0]    status_sum,
 9 
10     output    [15:0]    attr_sum,
11     output    [15:0]    attr_fibre_len
12 );
13     localparam REG_ATTR_SUM        = 0;
14     localparam REG_ATTR_FIBRE_LEN    = 1;
15 
16     localparam REG_STATUS_SUM    = 32;
17 
18     reg [6:0]  reg_addr         = 0;
19     reg [15:0] reg_attr_sum        = 2048;
20     reg [15:0] reg_attr_fibre_len    = 26000;
21 
22     assign attr_sum        = reg_attr_sum;
23     assign attr_fibre_len    = reg_attr_fibre_len;
24 
25     always @(posedge latch)
26     begin
27         if(we == 0)
28         begin
29             case (addr)
30                 REG_ATTR_SUM:        dout = reg_attr_sum;
31                 REG_ATTR_FIBRE_LEN:    dout = reg_attr_fibre_len;
32 
33                 REG_STATUS_SUM:        dout = status_sum;
34                 default:        dout = 0;
35             endcase
36         end
37     end
38 
39     always @(posedge latch)
40     begin
41         if(we == 0)
42         begin
43             reg_addr <= addr;
44         end
45         if(we == 1)
46         begin
47             case (reg_addr)
48                 REG_ATTR_SUM:        reg_attr_sum        <= din;
49                 REG_ATTR_FIBRE_LEN:    reg_attr_fibre_len    <= din;
50                 default:;
51             endcase
52         end
53     end
54 
55 endmodule
reg_array.v

【2】ARM端
【2.1】使用GPIO模拟SPI的驱动——简单、通用、速度慢。ui

  1 #include <linux/miscdevice.h>
  2 #include <linux/kernel.h>
  3 #include <linux/module.h>
  4 #include <linux/fs.h>
  5 #include <linux/types.h>
  6 #include <linux/moduleparam.h>
  7 #include <linux/ioctl.h>
  8 #include <linux/string.h>
  9 #include <mach/gpio.h>
 10 #include <mach/mux.h>
 11 #include <asm/uaccess.h>
 12 #include <linux/interrupt.h>
 13 #include "spi_fpga.h"
 14 
 15 static char *spi1_gpio_mux_name[] = {
 16     "GPIO0_7",
 17     "GPIO0_10",
 18     "GPIO0_8",
 19     //"GPIO0_9",
 20     "GPIO1_15"
 21 };
 22 
 23 #define PINMUX_SET_NUM        4
 24 
 25 static unsigned char mode_old_list[PINMUX_SET_NUM];
 26 static unsigned char flag_list[PINMUX_SET_NUM];
 27 
 28 #define SPI_CLK        GPIO_TO_PIN(0, 7)
 29 #define SPI_MOSI        GPIO_TO_PIN(0, 10)
 30 #define SPI_MISO        GPIO_TO_PIN(1, 15)    // GPIO_TO_PIN(0, 9)
 31 #define SPI_CS            GPIO_TO_PIN(0, 8)
 32 
 33 static enum SPI_GPIO_MODE__E     mode;
 34 static uint16_t            trans_len=0;
 35 int irq = 0;
 36 
 37 void spi_gpio_trans(void *user_addr)
 38 {
 39     int i=0;
 40     int j=0;
 41 
 42     uint8_t buf[trans_len];
 43     memset(buf,0,sizeof(buf));
 44 
 45     copy_from_user(buf,user_addr, sizeof(buf));
 46 #if 0
 47     printk("buf=");
 48     for(i = 0; i < trans_len; i++)
 49         printk("%02x,",buf[i]);
 50     printk("\n");
 51 #endif
 52     // 设置初始状态
 53     if(mode < 2)//CPOL=0
 54         gpio_set_value(SPI_CLK, 0);
 55     else
 56         gpio_set_value(SPI_CLK, 1);
 57     //片选初始为高
 58     gpio_set_value(SPI_CS, 1);
 59     // 传输开始,片选为低
 60     gpio_set_value(SPI_CS, 0);
 61 
 62     for(i = 0; i < trans_len; i++)
 63     {
 64         uint8_t byte_out = buf[i];
 65         uint8_t byte_in = 0;
 66         for(j = 0; j < 8; j++)
 67         {
 68             byte_in <<= 1;
 69             if(mode & 1) // CPHA=1,时钟的降低沿输出数据
 70             {
 71                 gpio_set_value(SPI_CLK, 1);
 72 
 73                 if(byte_out & 0x80)
 74                     gpio_set_value(SPI_MOSI, 1);
 75                 else
 76                     gpio_set_value(SPI_MOSI, 0);
 77 
 78                 gpio_set_value(SPI_CLK, 0);
 79 
 80                 if(gpio_get_value(SPI_MISO) != 0) byte_in |= 1;
 81             }
 82             else// 时钟的上升沿输出数据
 83             {
 84                 gpio_set_value(SPI_CLK, 0);
 85 
 86                 if(byte_out & 0x80)
 87                     gpio_set_value(SPI_MOSI, 1);
 88                 else
 89                     gpio_set_value(SPI_MOSI, 0);
 90 
 91                 gpio_set_value(SPI_CLK, 1);
 92 
 93                 if(gpio_get_value(SPI_MISO) != 0) byte_in |= 1;
 94             }
 95 
 96             byte_out <<= 1;
 97         }
 98         buf[i] = byte_in;
 99     }
100 #if 0
101     printk("buf=");
102     for(i = 0; i < trans_len; i++)
103         printk("%02x,",buf[i]);
104     printk("\n");
105 #endif
106     // 设置时钟线状态
107     if(mode < 2)//CPOL=0
108         gpio_set_value(SPI_CLK, 0);
109     else
110         gpio_set_value(SPI_CLK, 1);
111     // 传输结束,片选为高
112     gpio_set_value(SPI_CS, 1);
113 
114     copy_to_user(user_addr, buf, sizeof(buf));
115 }
116 //static int spi1_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
117  static long spi_ioctl(struct file *inode, unsigned int cmd, unsigned long arg)
118 {
119      /* 检测命令的有效性 */
120     //if (_IOC_TYPE(cmd) != SPI_GPIO_IOC__MAGIC)
121         //return -EINVAL;
122     //if (_IOC_NR(cmd) > SPI_GPIO_IOC__MAXNR)
123         //return -EINVAL;
124     //uint8_t in = 0;
125     switch(cmd)
126     {
127         case SPI_GPIO_IOC__SET_MODE:
128             mode = arg;
129             printk("mode=%d\n",mode);
130             break;
131         case SPI_GPIO_IOC__SET_LEN:
132             trans_len = arg;
133             printk("trans_len=%d\n",trans_len);
134             break;
135         case SPI_GPIO_IOC__TRANS:
136             spi_gpio_trans((void*)arg);
137 
138             break;
139         default:
140             break;
141     }
142     return 0;
143 }
144 
145 static int spi_open(struct inode *inode, struct file *filp)
146 {
147     return 0;
148 }
149 static int spi_release(struct inode *inode, struct file *filp)
150 {
151     return 0;
152 }
153 
154 static struct file_operations spi_fops =
155 {
156     .owner   = THIS_MODULE,
157     .open    = spi_open,
158     .release = spi_release,
159     .unlocked_ioctl = spi_ioctl
160 };
161 
162 static struct miscdevice misc = {
163     .minor = MISC_DYNAMIC_MINOR,
164     .name = "spi_fpga", //此名称将显示在/dev目录下面
165     .fops = &spi_fops,
166 };
167 
168 static int __init dev_init(void)
169 {
170     int i;
171     int ret;
172 
173     // 设置引脚复用
174     memset(flag_list, 0, PINMUX_SET_NUM);
175     for (i = 0; i < PINMUX_SET_NUM; i++) {
176         ret = davinci_cfg_reg_name(spi1_gpio_mux_name[i],
177             &mode_old_list[i], &flag_list[i]);
178     }
179     // 申请gpio占用
180     ret = gpio_request(SPI_CLK, "SPI_CLK");
181     if (ret)
182     {
183         pr_warning("Fail to request SPI_CLK PIN.\n");
184         goto INIT_FAILED0;
185     }
186     ret = gpio_request(SPI_MOSI, "SPI_MOSI");
187     if (ret)
188     {
189         pr_warning("Fail to request SPI_MOSI PIN.\n");
190         goto INIT_FAILED1;
191     }
192     ret = gpio_request(SPI_MISO, "SPI_MISO");
193     if (ret)
194     {
195         pr_warning("Fail to request SPI_MISO PIN.\n");
196         goto INIT_FAILED2;
197     }
198     ret = gpio_request(SPI_CS, "SPI_CS");
199     if (ret)
200     {
201         pr_warning("Fail to request SPI_CS PIN.\n");
202         goto INIT_FAILED3;
203     }
204     // 设置gpio方向和初始状态
205     gpio_direction_output(SPI_CLK, 0);
206     gpio_direction_output(SPI_MOSI, 0);
207     gpio_direction_output(SPI_CS, 1);
208     gpio_direction_input(SPI_MISO);
209 
210     printk ("spi_fpga initialized\n");
211     ret = misc_register(&misc);
212 
213     return ret;
214 INIT_FAILED3:
215     gpio_free(SPI_CS);
216 INIT_FAILED2:
217     gpio_free(SPI_MISO);
218 INIT_FAILED1:
219     gpio_free(SPI_MOSI);
220 INIT_FAILED0:
221     gpio_free(SPI_CLK);
222     return -1;
223 }
224 
225 static void __exit dev_exit(void)
226 {
227     int i = 0;
228     gpio_free(SPI_CLK);
229     gpio_free(SPI_MOSI);
230     gpio_free(SPI_MISO);
231     gpio_free(SPI_CS);
232 
233     for (i = 0; i < PINMUX_SET_NUM; i++) {
234         davinci_cfg_reg_name(spi1_gpio_mux_name[i],
235             &mode_old_list[i], &flag_list[i]);
236     }
237 
238     misc_deregister(&misc);
239     printk("spi_fpga unloaded\n");
240 }
241 
242 module_init(dev_init);
243 module_exit(dev_exit);
244 MODULE_LICENSE("GPL");
245 MODULE_AUTHOR("cjh");
spi_gpio.c

【2.2】应用程序spa

 1 #include <stdio.h>
 2 #include <sys/ioctl.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <fcntl.h>
 6 #include <stdint.h>
 7 #include <stdlib.h>
 8 #include <unistd.h>
 9 #include <string.h>
10 #include "../../../driver/spi_fpga/spi_fpga.h"
11 
12 int main()
13 {
14     ///////////////////////////////////////////////////////////////
15 
16     int fd = open("/dev/spi_fpga", O_RDWR);
17     if(fd < 0)
18     {
19         perror("open device failed\n");
20         return -1;
21     }
22     int ret = ioctl(fd, SPI_GPIO_IOC__SET_MODE, (int)SPI_GPIO_MODE__0);
23     if(ret < 0)
24     {
25         perror("set mode failed\n");
26         close(fd);
27         return -2;
28     }
29     ret = ioctl(fd, SPI_GPIO_IOC__SET_LEN, 4);
30     if(ret < 0)
31     {
32         perror("set len failed\n");
33         close(fd);
34         return -2;
35     }
36     uint8_t buf[4] = {0x2,0x6d,0x3b,0x0};// read reg[0]
37     ret = ioctl(fd, SPI_GPIO_IOC__TRANS, buf);
38     if(ret < 0)
39     {
40         perror("trans failed\n");
41         close(fd);
42         return -2;
43     }
44     printf("%02x,%02x,%02x,%02x\n",buf[3],buf[2],buf[1],buf[0]);
45 
46     close(fd);
47     return 0;
48 }
spi_fpga.c