OpenRisc-19-or1200下linux的i2c(一)

这篇 blog 介绍 I2C 的学习了,首先要在我们的 or1200_soc 上添加进来 I2C Controller ,到 opencores 社区上面找到 i2c_latest.tar.gz 这个工程包。

        解压得到如下文件目录:

OpenRisc-19-or1200下linux的i2c(一)_第1张图片

        注意啦,这里的i2c_top.v的顶层文件需要自己编写,因为需要在工程的更顶层将i2c_master_top.v里的输入输出信号组织成三态信号。

[plain] view plain copy print ?
  1. module i2c_top(  
  2.     //wishbone interfaces  
  3.     wb_clk_i, wb_rst_i, arst_i,   
  4.     wb_adr_i, wb_dat_i, wb_dat_o,  
  5.     wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_inta_o,  
  6.     i2c_scl, i2c_sda  
  7.     );  
  8.   
  9.     // wishbone signals  
  10.     input        wb_clk_i;     // master clock input  
  11.     input        wb_rst_i;     // synchronous active high reset  
  12.     input        arst_i;       // asynchronous reset  
  13.     input  [2:0] wb_adr_i;     // lower address bits  
  14.     input  [7:0] wb_dat_i;     // databus input  
  15.     output [7:0] wb_dat_o;     // databus output  
  16.     input        wb_we_i;      // write enable input  
  17.     input        wb_stb_i;     // stobe/core select signal  
  18.     input        wb_cyc_i;     // valid bus cycle input  
  19.     output       wb_ack_o;     // bus cycle acknowledge output  
  20.     output       wb_inta_o;    // interrupt request signal output  
  21.       
  22.     // i2c signals  
  23.     inout   i2c_scl;    //i2c clock signal  
  24.     inout   i2c_sda;    //i2c data signal  
  25.   
  26. i2c_master_top  i2c_master_top(  
  27.     //wishbone interfaces  
  28.     .wb_clk_i(wb_clk_i),   
  29.     .wb_rst_i(wb_rst_i),  
  30.     .arst_i(arst_i),  
  31.       
  32.     .wb_adr_i(wb_adr_i),  
  33.     .wb_dat_i(wb_dat_i),   
  34.     .wb_dat_o(wb_dat_o),  
  35.     .wb_we_i(wb_we_i),   
  36.     .wb_stb_i(wb_stb_i),   
  37.     .wb_cyc_i(wb_cyc_i),   
  38.     .wb_ack_o(wb_ack_o),  
  39.     .wb_inta_o(wb_inta_o),  
  40.       
  41.     .scl_pad_i(scl_pad_i),   
  42.     .scl_pad_o(scl_pad_o),   
  43.     .scl_padoen_o(scl_padoen_o),   
  44.     .sda_pad_i(sda_pad_i),   
  45.     .sda_pad_o(sda_pad_o),   
  46.     .sda_padoen_o(sda_padoen_o)  
  47.      );  
  48.        
  49. assign i2c_scl = scl_padoen_o ? 1'bz : scl_pad_o;    
  50. assign i2c_sda = sda_padoen_o ? 1'bz : sda_pad_o;   
  51. assign scl_pad_i = i2c_scl;   
  52. assign sda_pad_i = i2c_sda;  
  53.   
  54. endmodule  

        详细可以看看在改工程目录下的/doc说明文档,当中有改ipcore的使用说明。

        接着在or1200_soc中例化改ipcore,然后添加到wishbone总线上,再定义好使用的中断号。

        例化代码:

[plain] view plain copy print ?
  1. `ifdef I2C  
  2. wire [2:0] wb_i2c_adr_i;  
  3. wire [7:0] wb_i2c_dat_i;  
  4. wire [7:0] wb_i2c_dat_o;  
  5. wire [31:0] wb_i2c_dat32_i;  
  6. wire [31:0] wb_i2c_dat32_o;  
  7. wire [3:0]  wb_i2c_sel_i;  
  8. wire wb_i2c_stb_i;  
  9. wire wb_i2c_we_i;  
  10. wire wb_i2c_ack_o;  
  11. wire wb_i2c_cyc_i;  
  12. wire wb_i2c_inta_o;  
  13.   
  14. i2c_master_top i2c_master_top(  
  15.     //wishbone interfaces  
  16.     .wb_clk_i(clk_cpu_40),   
  17.     .wb_rst_i(wb_rst_pad_i),  
  18.     .arst_i(1'b1),  
  19.       
  20.     .wb_adr_i(wb_i2c_adr_i),  
  21.     .wb_dat_i(wb_i2c_dat_i),   
  22.     .wb_dat_o(wb_i2c_dat_o),  
  23.     .wb_we_i(wb_i2c_we_i),   
  24.     .wb_stb_i(wb_i2c_stb_i),   
  25.     .wb_cyc_i(wb_i2c_cyc_i),   
  26.     .wb_ack_o(wb_i2c_ack_o),  
  27.     .wb_inta_o(pic_ints[`APP_INT_I2C]),  
  28.       
  29.     //i2c interface  
  30.     .scl_pad_i(scl_pad_i),   
  31.     .scl_pad_o(scl_pad_o),   
  32.     .scl_padoen_o(scl_padoen_o),   
  33.     .sda_pad_i(sda_pad_i),   
  34.     .sda_pad_o(sda_pad_o),   
  35.     .sda_padoen_o(sda_padoen_o)       
  36.     );  
  37.       
  38. assign i2c_scl = scl_padoen_o ? 1'bz : scl_pad_o;    
  39. assign i2c_sda = sda_padoen_o ? 1'bz : sda_pad_o;   
  40. assign scl_pad_i = i2c_scl;  
  41. assign sda_pad_i = i2c_sda;   
  42.       
  43. assign wb_i2c_dat32_o[7:0]   = (wb_i2c_sel_i[0] == 1'b1) ? wb_i2c_dat_o : 8'h0;  
  44. assign wb_i2c_dat32_o[15:8]  = (wb_i2c_sel_i[1] == 1'b1) ? wb_i2c_dat_o : 8'h0;  
  45. assign wb_i2c_dat32_o[23:16] = (wb_i2c_sel_i[2] == 1'b1) ? wb_i2c_dat_o : 8'h0;  
  46. assign wb_i2c_dat32_o[31:24] = (wb_i2c_sel_i[3] == 1'b1) ? wb_i2c_dat_o : 8'h0;  
  47.   
  48. assign wb_i2c_dat_i = wb_i2c_dat32_i[7:0];  
  49.   
  50. `else  
  51. assign pic_ints[`APP_INT_I2C] = 'b0;  
  52. `endif/*endif I2C*/  
  53.   
  54. /* the comment section below just instance for wb_conbus module */  
  55. /*  
  56. `ifdef I2C  
  57.     .s5_dat_i   (wb_i2c_dat32_o),  
  58.     .s5_dat_o   (wb_i2c_dat32_i),  
  59.     .s5_adr_o   (wb_i2c_adr_i),  
  60.     .s5_sel_o   (wb_i2c_sel_i),  
  61.     .s5_we_o    (wb_i2c_we_i),  
  62.     .s5_cyc_o   (wb_i2c_cyc_i),  
  63.     .s5_stb_o   (wb_i2c_stb_i),  
  64.     .s5_ack_i   (wb_i2c_ack_o),  
  65.     .s5_err_i   (1'b0),  
  66.     .s5_rty_i   (1'b0),   
  67. //  .s5_cab_i   (),  
  68.   
  69. for switch cross bus :  
  70.     // .slave5_sel_addr ( `I2C_BASE_ADDR ),  
  71.       
  72.     .wbs5_adr_i( wb_i2c_adr_i ),  
  73.     .wbs5_bte_i(  ),  
  74.     .wbs5_cti_i(  ),  
  75.     .wbs5_cyc_i( wb_i2c_cyc_i ),  
  76.     .wbs5_dat_i( wb_i2c_dat32_i ),  
  77.     .wbs5_sel_i( wb_i2c_sel_i ),  
  78.     .wbs5_stb_i( wb_i2c_stb_i ),  
  79.     .wbs5_we_i( wb_i2c_we_i ),  
  80.     .wbs5_ack_o( wb_i2c_ack_o ),  
  81.     .wbs5_err_o( 'b0 ),  
  82.     .wbs5_rty_o( 'b0 ),  
  83.     .wbs5_dat_o( wb_i2c_dat32_o ),  
  84. */  

        中断号和地址:

[plain] view plain copy print ?
  1. /* Interrupts */   
  2. `define APP_INT_RES1        1:0    
  3. `define APP_INT_UART        2   
  4. `define APP_INT_KEY     3  
  5. `define APP_INT_ETH     4  
  6. `define APP_INT_I2C         5  
  7. `define APP_INT_VGA_LCD 6  
  8. `define APP_INT_RES     19:7  
  9.   
  10. /* Peripheral Addr ,modify by manual */  
  11. `define FLASH_BASE_ADDR 4'hf        //slave X address ,connect to FLASH  
  12. `define SDRAM_BASE_ADDR 4'h0        //slave X address ,connect to DDR_SDRAM  
  13. `define UART_BASE_ADDR  8'h90   //slave X address ,connect to UART  
  14. `define GPIO_BASE_ADDR  8'h91   //slave X address  
  15. `define ETH_BASE_ADDR       8'h92   //slave X address ,connect to ETH  
  16. `define VGA_BASE_ADDR       8'h95   //slave X address, connect to VGA/LCD  
  17. `define DMA_BASE_ADDR   8'hxx   //slave X address,   
  18. `define SRAM_BASE_ADDR  8'hxx   //slave X address ,connect to SRAM  
  19. `define SD_CARD_BASE_ADDR   8'h94   //slave X address ,connect to sd_card  
  20. `define I2C_BASE_ADDR       8'h93   //slave X address ,connect to i2c device   

        添加到wishbone总线:

[plain] view plain copy print ?
  1. wb_switch_b3 #(  
  2.     .slave0_sel_addr ( `FLASH_BASE_ADDR ),  
  3.     .slave1_sel_addr ( `SDRAM_BASE_ADDR ),  
  4.     .slave2_sel_addr ( `UART_BASE_ADDR ),  
  5.     .slave3_sel_addr ( `ETH_BASE_ADDR ),  
  6.     .slave4_sel_addr ( `GPIO_BASE_ADDR ),  
  7.     .slave5_sel_addr ( `I2C_BASE_ADDR ),  
  8.     .slave6_sel_addr ( 'hfffffffe ),  
  9.     .slave7_sel_addr ( 'hfffffffe )  
  10.     )  
  11.       
  12.     wb_switch_b3(  
  13.     // Clocks, resets  
  14.     .wb_clk(clk_cpu_40),  
  15.     .wb_rst(wb_rst_pad_i),  
  16.         ........  
  17.     // Slave 5 Interface ,connect to simple_i2c  
  18.     .wbs5_adr_i( wb_i2c_adr_i ),  
  19.     .wbs5_bte_i(  ),  
  20.     .wbs5_cti_i(  ),  
  21.     .wbs5_cyc_i( wb_i2c_cyc_i ),  
  22.     .wbs5_dat_i( wb_i2c_dat32_i ),  
  23.     .wbs5_sel_i( wb_i2c_sel_i ),  
  24.     .wbs5_stb_i( wb_i2c_stb_i ),  
  25.     .wbs5_we_i( wb_i2c_we_i ),  
  26.     .wbs5_ack_o( wb_i2c_ack_o ),  
  27.     .wbs5_err_o( 'b0 ),  
  28.     .wbs5_rty_o( 'b0 ),  
  29.     .wbs5_dat_o( wb_i2c_dat32_o ),  
  30.         ........  
  31. );  

        OKsynthesize之,注意稍微看看时序够不够即可。

        与前一节添加GPIO Controller的步骤一样,进入虚拟机中的关于SOC的设备树文件目录openrisc-3.1\arch\openrisc\boot\dts添加关于板子上I2C器件的设备描述

OpenRisc-19-or1200下linux的i2c(一)_第2张图片

        open之,在其中加入关于板子上的I2C器件的描述,例如我板子上有个AT24c08,所以我就添加eeprom设备,形式如下:

[cpp] view plain copy print ?
  1. i2c0: ocores@93000000 {  
  2.              compatible = "opencores,i2c-ocores";  
  3.              reg = <0x93000000 0x8>;  
  4.              interrupts = <5>;  
  5.                
  6.              regstep = <1>;  
  7.              clock-frequency = <40000000>;  
  8.                
  9.              #address-cells = <1>;  
  10.              #size-cells = <0>;  
  11.              eeprom@54 {  
  12.                     compatible = "at24";  
  13.                     reg = <0x54>;  
  14.     };  
  15. };  

        至于关于其中编写方法后述。

        然后,在命令行中进入linux源码目录,然后需要配置内核加入上面添加的I2C的总线驱动,这是因为关于这个ipcore的驱动社区上的大虾已经编写好,现在先熟悉和使用,在后面再去详细分析这个总线驱动如何编写。


        内核配置,进入General setup

OpenRisc-19-or1200下linux的i2c(一)_第3张图片

        将Prompt for development and/or incomplete code/drivers打开

OpenRisc-19-or1200下linux的i2c(一)_第4张图片

        Exit后,到Device Drivers项中


        将I2C support选择编入内核中


        进入,将I2C device interface选中编入内核,我们第一个应用层的测试文件就基于内核编写好的设备接口文件来写


        然后选择I2C Hardware Bus support

OpenRisc-19-or1200下linux的i2c(一)_第5张图片

        将OpenCores I2C Controller选择编入内核

OpenRisc-19-or1200下linux的i2c(一)_第6张图片

        好,最后exit出来选择保存,重新make

OpenRisc-19-or1200下linux的i2c(一)_第7张图片

        之后的步骤按照之前移植linux的步骤一样,生成uImage镜像,然后下载到开发板上面即可。

        然后在boot linux的过程和进入/dev目录下可以看到i2c bus driver和生成设备文件i2c-x

OpenRisc-19-or1200下linux的i2c(一)_第8张图片

        至此,i2c的总线驱动就添加到内核了。

        至于为什么一直强调是i2c的总线驱动呢······再敲代码之前我们先看看csdn上的这两篇blog,看看华清的hongtao_liu老师是怎么说明linux下的I2C

        http://blog.csdn.net/hongtao_liu/article/details/4964244

        http://blog.csdn.net/hongtao_liu/article/details/5260739


        根据我自己的理解:

        1.对于i2c controller来说,编写的驱动程序称为总线驱动,例如上面在or1200_soc中添加的i2c控制器ipcore,为之编写的驱动程序就是总线驱动。

        2.对于挂在i2c总线上的slave device来说,为之编写的驱动程序称为设备驱动,例如我板子上挂在这个ipcore上的AT24c08eeprom

        3.linux下的i2c子系统中还有一个i2c核心模块来为总线驱动和设备驱动服务,完成注册,删除······功能。

        现在说明一下:在openrisc-3.1\drivers\i2c\busses目录下的i2c-ocores.c文件为soc上添加的i2c controller提供了总线驱动,即在刚才的内核配置中已经选择编入了内核,所以可以略过总线驱动的编写,在下节我们着重分析这个总线驱动。

OpenRisc-19-or1200下linux的i2c(一)_第9张图片

        现在可以先打开这个文件,里面的comment中就有关于device-tree文件如何加入i2c device描述的说明

[cpp] view plain copy print ?
  1. /* 
  2.  * Device tree configuration: 
  3.  * 
  4.  * Required properties: 
  5.  * - compatible      : "opencores,i2c-ocores" 
  6.  * - reg             : bus address start and address range size of device 
  7.  * - interrupts      : interrupt number 
  8.  * - regstep         : size of device registers in bytes 
  9.  * - clock-frequency : frequency of bus clock in Hz 
  10.  *  
  11.  * Example: 
  12.  * 
  13.  *  i2c0: ocores@a0000000 { 
  14.  *              compatible = "opencores,i2c-ocores"; 
  15.  *              reg = <0xa0000000 0x8>; 
  16.  *              interrupts = <10>; 
  17.  * 
  18.  *              regstep = <1>; 
  19.  *              clock-frequency = <20000000>; 
  20.  * 
  21.  * -- Devices connected on this I2C bus get 
  22.  * -- defined here; address- and size-cells 
  23.  * -- apply to these child devices 
  24.  * 
  25.  *              #address-cells = <1>; 
  26.  *              #size-cells = <0>; 
  27.  * 
  28.  *              dummy@60 { 
  29.  *                     compatible = "dummy"; 
  30.  *                     reg = <60>; 
  31.  *              }; 
  32.  *  }; 
  33.  * 
  34.  */  

        在openrisc-3.1\drivers\i2c目录下的i2c-dev.c文件中,linux为我们提供了虚拟的统一的设备文件接口,如果我们懒得去编写设备驱动程序时,可以利用这个接口直接在应用层上完成对i2c总线的slave device操作。

OpenRisc-19-or1200下linux的i2c(一)_第10张图片

        所以在这一节当中,我们不编写总线驱动和设备驱动,先运用系统提供的文件接口进行操作,具体的代码主要参考hongtao_liu老师提供的代码。

[cpp] view plain copy print ?
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <stdlib.h>  
  4. #include <fcntl.h> // open()   
  5. #include <sys/ioctl.h>  
  6. #include <unistd.h> // read() write() close() usleep()  
  7. #include <linux/types.h>   
  8. #include <linux/i2c.h> // i2c_msg   
  9. #include <linux/i2c-dev.h> // i2c_rdwr_ioctl_data  
  10.   
  11. #define DEVICE_NAME "/dev/i2c-0"  
  12.   
  13. #define MAX_MSG 2  
  14. #define EEPROM_ADDR 0x54  
  15.   
  16. //--------------------------------------------- main -----------------------------------------------------  
  17. int main(){  
  18.     int fd;  
  19.     int ret;  
  20.     struct i2c_rdwr_ioctl_data eeprom_data;  
  21.       
  22.     /* open device file */  
  23.     printf("\nstart simple_i2c application test ! \n");  
  24.     fd = open(DEVICE_NAME, O_RDWR);  
  25.         if (fd == -1){  
  26.                 printf("open device %s error !\n",DEVICE_NAME);  
  27.         }  
  28.     else   
  29.         printf("open device file successful, fd = %d\n",fd);  
  30.           
  31.     /* set parameter for msg */  
  32.     eeprom_data.nmsgs = MAX_MSG;  
  33.     eeprom_data.msgs=(struct i2c_msg *)malloc(eeprom_data.nmsgs * sizeof(struct i2c_msg));  
  34.     if( !eeprom_data.msgs ){  
  35.         printf("malloc error...\n");  
  36.         return -1;  
  37.     }  
  38.           
  39.     /* timeout & retry */  
  40.     ioctl(fd, I2C_TIMEOUT, 1);  
  41.     ioctl(fd, I2C_RETRIES, 2);  
  42.           
  43.     /* write data to eeprom */  
  44.     eeprom_data.nmsgs = 1;  
  45.       
  46.     (eeprom_data.msgs[0]).len = 2;  
  47.     (eeprom_data.msgs[0]).addr = EEPROM_ADDR;  
  48.     (eeprom_data.msgs[0]).flags = 0;  
  49.     (eeprom_data.msgs[0]).buf = (unsigned char*)malloc(2);  
  50.     (eeprom_data.msgs[0]).buf[0] = 0x10;  
  51.     (eeprom_data.msgs[0]).buf[1] = 0x50;  
  52.        
  53.     ret = ioctl(fd, I2C_RDWR, (unsigned long)&eeprom_data);  
  54.     if(ret < 0){  
  55.         printf("ioctl error...\n");  
  56.     }  
  57.       
  58.     /* read data from eeprom */  
  59.     eeprom_data.nmsgs = 2;  
  60.       
  61.     (eeprom_data.msgs[0]).len = 1;  
  62.     (eeprom_data.msgs[0]).addr = EEPROM_ADDR;  
  63.     (eeprom_data.msgs[0]).flags = 0;  
  64.     (eeprom_data.msgs[0]).buf[0] = 0x10;  
  65.       
  66.     (eeprom_data.msgs[1]).len = 1;  
  67.     (eeprom_data.msgs[1]).addr = EEPROM_ADDR;  
  68.     (eeprom_data.msgs[1]).flags = I2C_M_RD;  
  69.     (eeprom_data.msgs[1]).buf = (unsigned char*)malloc(1);  
  70.     (eeprom_data.msgs[1]).buf[0] = 0;  
  71.   
  72.     ret = ioctl(fd, I2C_RDWR, (unsigned long)&eeprom_data);  
  73.     if(ret < 0){  
  74.         printf("ioctl error...\n");  
  75.     }  
  76.       
  77.     printf("buff[0]=%x\n",(eeprom_data.msgs[1]).buf[0]);  
  78.           
  79.     /* close device file */  
  80.     ret = close(fd);  
  81.     printf ("ret=%d\n",ret);  
  82.     printf ("end simple_i2c test...\n");  
  83.       
  84.     /*  */  
  85.     return 0;     
  86. }  

        编写完后在虚拟机上编译,老规矩,在板子上lrz后修改文件属性

OpenRisc-19-or1200下linux的i2c(一)_第11张图片

        然后可以多次修改其中写入地址和数据的代码,看看是否能正确读写

OpenRisc-19-or1200下linux的i2c(一)_第12张图片

        至此,有时间修改一下这个测试文件成比较实用的文件,下节对这个ipcorei2c bus driver的分析,即对openrisc-3.1\drivers\i2c\busses目录下的i2c-ocores.c的总线驱动稍作分析,当是学习如何编写总线驱动。

        OK,又一周了,追追动漫去了~,最近手头上有点忙了,苦逼的程序员真是伤不起啊,希望有心力的人接着弄下去吧,下次都不知道什么时候有时间更新这个系列的文章了,但是会写写别的文章~bye~bye

你可能感兴趣的:(OpenRisc-19-or1200下linux的i2c(一))