Nios II开发--按键中断处理


文章摘要:
本文描述了基于NIOS II系统的按键中断处理;


硬件平台:EP4CE6F17C8
开发环境:Quartus II 13.1/Nios II 13.1


配置Qsys添加PIO模块

重新编译并下载


重新生成BSP


新建Nios项目工程


示例代码:

/*
 *  main.c
 *  Created on: 2017-4-18
 *  Author: alex
 */
#include "system.h"
#include "sys/alt_irq.h"

// 定义寄存器,采用比较通用的寄存器定义方法来实现读写访问
#define rLEDS_PORT        *((volatile int *)(PIO_LEDS_BASE + (0 << 2)))
#define rKEYS_IRQ_MASK    *((volatile int *)(PIO_KEYS_BASE + (2 << 2)))
#define rKEYS_EDGE_CAP    *((volatile int *)(PIO_KEYS_BASE + (3 << 2)))

char val = 0x00;

// 中断服务函数
void isr_keys(void* context)
{
    rKEYS_EDGE_CAP = 0; // 清除中断
    val = val + 1;
}

// 主函数入口
int main(void)
{
    // 注册中断
    alt_ic_isr_register(
            PIO_KEYS_IRQ_INTERRUPT_CONTROLLER_ID,
            PIO_KEYS_IRQ,    // 中断向量
            isr_keys,        // 中断服务函数
            0,               // 中断服务函数参数
            0);              // flag

    rKEYS_IRQ_MASK = 0x03;   // 中断屏蔽(使能)
    rKEYS_EDGE_CAP = 0;      // 清队中断标识位

    while(1)
    {
        rLEDS_PORT = val;
    }

    return 0;
}

- 阅读全文 -

Nios II开发--流水灯


文章摘要:
本文以流水灯为例,描述了基于NIOS II系统的软件开发流程;


硬件平台:EP4CE6F17C8
开发环境:Nios II 13.1


生成BSP(Board Support Package 板级支持包)

1.打开Nios II 13.1软件,新建BSP工程
File --> New --> Nios II Board Support Package

生成BSP


2.修改BSP
右键项目工程 --> Nios II --> BSP Editor...

完成后点击Generate,并退出。


3.编译工程

右键项目工程 --> Build Project


新建应用工程

File --> New --> Nios II Application
应用项目工程


创建源文件:

右键项目工程 --> New --> Source File

/*
 *  main.c
 *  Created on: 2017-4-18
 *  Author: alex
 */

#include "altera_avalon_pio_regs.h"
#include "system.h"

// 延时程序
void delay(int n)
{
    while(n--);
}

// 主程序
int main(void)
{
    while(1){
        IOWR_ALTERA_AVALON_PIO_DATA(PIO_LEDS_BASE,0x01);
        delay(500000);
        IOWR_ALTERA_AVALON_PIO_DATA(PIO_LEDS_BASE,0x02);
        delay(500000);
        IOWR_ALTERA_AVALON_PIO_DATA(PIO_LEDS_BASE,0x04);
        delay(500000);
        IOWR_ALTERA_AVALON_PIO_DATA(PIO_LEDS_BASE,0x08);
        delay(500000);
    }
    return 0;
}

编译工程:

右键项目工程 --> Build Project


运行配置:
Run --> Run Configures...

Project
配置

Target Connection
请输入图片描述

点击Refresh Connection,直到显示设备连接(如果一直不显示,则可能是Niso配置有问题);


运行测试:

Run --> Run


PIO接口说明:
PIO接口定义于altera_avalon_pio_regs.h中(bsp/drivers/inc):

IORD_ALTERA_AVALON_PIO_DATA  读数据寄存器
IOWR_ALTERA_AVALON_PIO_DATA  写数据寄存器

IORD_ALTERA_AVALON_PIO_DIRECTION 读方向寄存器
IOWR_ALTERA_AVALON_PIO_DIRECTION 写方向寄存器

IORD_ALTERA_AVALON_PIO_IRQ_MASK  读中断掩码寄存器
IOWR_ALTERA_AVALON_PIO_IRQ_MASK  写中断掩码寄存器

IORD_ALTERA_AVALON_PIO_EDGE_CAP 读中断标识寄存器
IOWR_ALTERA_AVALON_PIO_EDGE_CAP 写中断标识寄存器
     
IOWR_ALTERA_AVALON_PIO_SET_BITS      位操作置位    
IOWR_ALTERA_AVALON_PIO_CLEAR_BITS    位操作清零

实际上此操作接口比较繁琐,不如直接定义寄存器地址指针,然后直接操作更简单:

#define LEDS_PORT    *((volatile int *)(PIO_LEDS_BASE + 0))
LEDS_PORT = x;

- 阅读全文 -

FPGA开发--Nios II最小系统搭建


文章摘要:
网上对于FPGA嵌入CPU的讨论颇多,硬核、固核、软核各有千秋,这里就不做具体分析优劣,也不太建议做过多的争执,只有先彻底的了解其相关技术,才能做出比较中肯的评价;

做为技术层面来说,不能太主观或者过于轻信某一种技术的好于坏,关键是看攻城狮用的场合不合适,多掌握一门手艺,毕竟不是坏事,软核的唯一好处就是,你如果觉得不想用,就可以不用,把节省下的逻辑单元用来干其他的事,想用的话,也很容易配置进去当然前提是你要知道怎么来做才行;

任何一种技术,如果掌握了其技能,你可以有选择不用的权利,但是如果没有掌握,在选择上就会比较局限,所以,个人认为,做为一个技术匠人,应该以包容的心态去学习每一种技术,也只有这样,在创作的时候,才更容易得心应手;


硬件平台:EP4CE6F17C8
开发环境:Quartus II 13.1


创建一个Quartus II的项目工程

工程名为:nios_test


通过Qsys向导来新建NIOS2;
通过Tools-->Qsys菜单启动Qsys软件;


配置系统时钟
打开Qsys软件时,系统会建立一个默认工程,并自动创建了一个clock组件(由此可见时钟的重要性);
双击时钟即可参数进行配置,时钟设计为50MHz,与外部晶振的频率一致,其他采用默认值;

右键-->Rename修改名称为clock

注意事项:
配置的时钟参数必须要和实际的一致,为了简单起见,暂不采用PLL时钟,而采用50MHz外部晶振;


添加cpu核

Library --> 
    Embedded Processors --> 
        Nios II Processor

Nios II Core: Nios II/s

其他项目先采用默认值(后边还需要修改)

点击Finish完成创建;

右键-->Rename修改名称为 cpu.

信号连接:
将时钟信号(clk)连接至clock模块的clk输出信号;
将复位信号(reset_n)连接至clock模块的clk_reset输出信号;


添加片内RAM

Library --> 
    Memories and Memory Controllers --> 
        On-Chip --> 
            On-Chip Menory(RAM or ROM)

Type选择 RAM(Writable)

Data width: 32.(数据宽度32位)
Total memory size: 20480 bytes(容量20K).
其他参数暂时选择默认;

信号连接:
将时钟信号(clk1)连接至clock模块的clk输出信号;
将复位信号(reset1)连接至clock模块的clk_reset输出信号;
将s1信号,连接分别连接至cpu的数据总线(data_master)和指令总线(instruction_master);

注意事项:
1.虽然根据数据手册显示EP4CE6一共有270Kb(33KB)RAM,但是在布线时会提示部分RAM无法布局,所以这里先用20KB;
2.由于程序在RAM中运行,所以指令总线也需要连接;


添加JTAG-UART

Library --> 
    Interface Protocols --> 
        Serial -->
            JTAG UART

Buffer depth(bytes): 16
不选 Construct using registers instead of memory blocks

信号连接:
将时钟信号(clk)连接至clock模块的clk输出信号;
将复位信号(reset)连接至clock模块的clk_reset输出信号;
将avalon_jtag_slave信号连接至cpu的数据总线(data_master);


添加LED外设

Library --> 
    Peripherals --> 
        Microcontroller Peripherals --> 
            PIO (Parallel I/O)

Width(1-32bits): 数据宽度,选择4,因为硬件只连接了4个LED端口;
Direction: 方向,选择Output(输出);
Output Port Reset Value: 复位值,选择全0;

其他值默认;

信号连接:
将时钟信号(clk)连接至clock模块的clk输出信号;
将复位信号(reset)连接至clock模块的clk_reset输出信号;
将s1信号连接至cpu的数据总线(data_master);

引脚导出:

双击external_connection的Export项目,导出引脚;


请输入图片描述


重新设置复位与异常向量
双击cpu项,打开配置界面,指定复位和异常向量们于ram区;

请输入图片描述


保存Qsys工程:
保存在项目工程目录下的core目录下,名称为nios_mini.qsys;

分配地址:
System --> Assign Base Addresses

生成
Generate
--> Generate...
会产生一些警告,暂时不用理;


获取例化代码

Generate --> HDL Example


生成顶层模块

  1. 关闭Qsys回到Quartus II

2.将core/nios_mini.qsys文件添加至工程

3.建立顶层文件,名称为nios_test.v,内容如下:

module nios_test(
    input  clk,
    input  rst_n,
    output leds
);

// 例化nios_mini
nios_mini u0 (
    .clock                      (clk), 
    .out_port_from_the_pio_leds (leds), 
    .reset_n                    (rst_n)   
);

endmodule

分析综合,分配引脚,全编译,下载

- 阅读全文 -

FPGA开发--AT24C读写


文章摘要:
本文主要是用于对I2C模块的测试程序,实现对AT24C进行读测试;

重点内容: SingleTap应用,AT24C读写时序;


硬件平台:EP4CE6F17C8
开发环境:Quartus II 13.1


AT24C读写时序图

AT24C读写时序图


测试代码:

module at24c02(
    clk,        // 模块时钟
    rst_n,      // 模块复位(低电平有效)
    scl,        // 时钟引脚
    sda,        // 数据引脚(双向)     
    rdata       // 接收到的数据
);

input  clk;     // 模块时钟
input  rst_n;   // 模块复位(低电平有效)
output scl;     // 时钟引脚
inout  sda;     // 数据引脚(双向)
output[7:0] rdata;
//-----------------------------------------------
localparam T1SEC   = 32'd50_000_000;
//-----------------------------------------------
reg  i2c_start_req;
reg  i2c_wr_req;
reg  i2c_rd_req;
reg  i2c_stop_req;
reg[7:0] i2c_wdata;
wire i2c_done;

reg[3:0] cstate;
reg[3:0] nstate;
reg init_flag;
//-----------------------------------------------
reg [31:0] timer;
// 计时时钟
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        timer <= 0;
    end
    else if(timer == T1SEC) begin
        timer <= 0;    
    end
    else begin
        timer <= timer + 1'b1;        
    end
end
//-----------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        i2c_wr_req    <= 0;
        i2c_rd_req    <= 0;
        i2c_start_req <= 0;
        i2c_stop_req  <= 0;
        cstate        <= 0;
    end
    
    else begin
        case(cstate)
            0: begin
                if(timer == T1SEC) begin                      
                    cstate    <= cstate + 1'b1;
                    init_flag <= 0;
                end                   
            end    
            
            // 发送起始位及设备地址
            1: begin
                if(init_flag == 0) begin
                    i2c_wdata <= 8'b1010_000_0; // 设备地址
                    i2c_start_req <= 1'b1;          // 启动发送
                    init_flag <= 1;
                end
                else if(i2c_done) begin    
                    init_flag <= 0;
                    cstate    <= cstate + 1'b1;
                end
                else begin
                    i2c_start_req <= 0;  
                end
            end
            
            // 发送字地址
            2: begin     
                if(init_flag == 0) begin
                    init_flag  <= 1;
                    i2c_wr_req <= 1'b1;
                    i2c_wdata  <= 8'b0000_0000; 
                end
                else if(i2c_done) begin    
                    init_flag <= 0;
                    cstate    <= cstate + 1'b1;
                end
                else begin
                    i2c_wr_req <= 0;  
                end
            end
            
            // 重启总线及发送设备地址
            3: begin
                if(init_flag == 0) begin
                    init_flag <= 1;
                    i2c_wdata <= 8'b1010_000_1; 
                    i2c_start_req <= 1'b1;
                end
                else if(i2c_done) begin 
                    init_flag <= 0;
                    cstate    <= cstate + 1'b1;
                end
                else begin
                    i2c_start_req <= 0;  
                end
            end            
            
            // 读操作
            4: begin
                if(init_flag == 0) begin
                    init_flag <= 1;
                    i2c_rd_req <= 1'b1;     // 读操作
                end
                else if(i2c_done) begin                      
                    cstate    <= cstate + 1'b1;
                    init_flag <= 0;
                end
                else begin
                    i2c_rd_req <= 0;  
                end
            end
            
            // 发送停止位
            5: begin
                if(init_flag == 0) begin
                    init_flag    <= 1;
                    i2c_stop_req <= 1'b1;     // 读操作
                end
                else if(i2c_done) begin
                    init_flag <= 0;
                    cstate    <= 0;
                end
                else begin
                    i2c_stop_req <= 0;  
                end
            end            
            default: begin
                cstate <= 0;
            end        
        endcase  
    end          
end

// 例化模块
i2c_comm m1(
    .clk(clk),        // 模块时钟
    .rst_n(rst_n),    // 模块复位(低电平有效)
    .scl(scl),        // 时钟引脚
    .sda(sda),        // 数据引脚(双向)
    
    .start_req(i2c_start_req),  // 发送起始位及设备地址
    .stop_req(i2c_stop_req),    // 发送停止位
    .wr_req(i2c_wr_req),        // 发送数据命令
    .rd_req(i2c_rd_req),        // 读取数据命令
    
    .rdata(rdata),              // 读取数据
    .wdata(i2c_wdata),          // 待写数据
    .done(i2c_done),            // 单步操作完成标志
    .busy()                     // 繁忙信号
);

endmodule

添加SingleTap测试文件:

1.采样时钟设置为timer[1],由于存储深度的问题,如果直接采用clk做为采样时钟,则存储不够一个完整的流程,所以需要降低采样频率来现;
2.添加SCL信号;
3.添加SDA信号,并设置SDA下降沿触发;
4.添加rdata信号;

AT24C SingleTap


测试结果:

点击Run Analysis进行采集,自动在SDA下降(起始位)沿进行触发;

AT24C测试结果波形

- 阅读全文 -

FPGA开发--I2C读写时序


文章摘要:
本文实现了类似于单片机的外设的I2C读写模块,将I2C各步骤独立出来,可以更清晰的描述I2C时序及工作原理,也使代码具有更发了好的通用性;

重点内容: 三段式状态机,双向IO,I2C时序;


硬件平台:EP4CE6F17C8
开发环境:Quartus II 13.1


时序图:

请输入图片描述

起始位:SCL高电平时,SDA下跳;
停止位:SCL高电平时,SDA上跳;
数据位:SCL低电平时,SDA可改变状态,SCL上跳时传输;
应答位: 0 - ACK, 1 - NAK(只有主机才允许发送NAK)


I2C模块

/*
 *  功能描述:I2C接口模块
 */
module i2c_comm(
    clk,        // 模块时钟
    rst_n,      // 模块复位(低电平有效)
    scl,        // 时钟引脚
    sda,        // 数据引脚(双向)
    
    start_req,  // 发送起始位
    wr_req,     // 发送请求
    rd_req,     // 接收请求
    stop_req,   // 发送停止位
    
    wdata,      // 待发数据
    rdata,      // 接收数据
    busy,       // 空闲(0)/繁忙(1)
    done        // 操作完成(脉冲信号)
);

input  clk;     // 模块时钟
input  rst_n;   // 模块复位(低电平有效)
output scl;     // 时钟引脚
inout  sda;     // 数据引脚(双向)

input start_req;    // 发送直始位
input wr_req;       // 发送数据
input rd_req;       // 读取数据
input stop_req;     // 发送停止位

input [7:0]wdata;   // 待发数据
output[7:0]rdata;   // 待发数据
output busy;        // 操作完成(空闲)
output reg done;
//-----------------------------------------
parameter I2C_COUNT = 20'd500;  // 100KHz = 10us

parameter S_IDLE       = 4'd0;
parameter S_START      = 4'd1;
parameter S_SEND_DATA  = 4'd2;
parameter S_RECV_DATA  = 4'd3;
parameter S_WAIT_ACK   = 4'd4;
parameter S_SEND_ACK   = 4'd5;
parameter S_STOP       = 4'd6;
//-----------------------------------------
reg [3:0] nstate;   // 后续状态
reg [3:0] cstate;   // 当前状态
//-----------------------------------------
reg isout;
reg scl_r;
reg sda_r;
reg [7:0]rdata_r;
reg [3:0] bits;
reg[19:0] i2c_cnt;
//-----------------------------------------
assign scl  = scl_r;
assign sda  = isout?sda_r:1'bz;  
assign busy = (cstate == S_IDLE)?1'b0:1'b1;
assign rdata = rdata_r;
//-----------------------------------------
// 传输计时逻辑
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        i2c_cnt <= 0;
    end
    else if(cstate == S_IDLE) begin
        i2c_cnt <= 0;
    end
    else if(i2c_cnt == I2C_COUNT) begin
        i2c_cnt <= 0;
    end    
    else begin
        i2c_cnt <= i2c_cnt + 1'b1;
    end 
end

//-----------------------------------------
// 三段状态机第一段(标准代码)
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        cstate <= S_IDLE;
    end
    else begin
        cstate <= nstate;
    end 
end
//-----------------------------------------
// 三段状态机第二段(状态转移)
always @(*) begin
    case(cstate)
        S_IDLE: begin
            if(start_req) begin
                nstate = S_START;       // 启动总线
            end    
            else if(stop_req) begin
                nstate = S_STOP;        // 停止总线
            end 
            else if(wr_req) begin
                nstate = S_SEND_DATA;   // 发送数据
            end            
            else if(rd_req) begin
                nstate = S_RECV_DATA;   // 发送数据
            end
            else begin
                nstate = S_IDLE;
            end
        end   
        
        // 发送起始位
        S_START: begin            
            if(i2c_cnt == I2C_COUNT) begin
                nstate  = S_SEND_DATA;  // 发送完起始位,发送设备地址
            end            
            else begin
                nstate  = S_START; 
            end
        end          
        
        // 发送数据
        S_SEND_DATA: begin
            if(bits == 4'd8) begin
                nstate  = S_WAIT_ACK;  // 发送完数据,接收ACK
            end            
            else begin
                nstate  = S_SEND_DATA; 
            end
        end
        // 等待设备响应
        S_WAIT_ACK: begin
            if(i2c_cnt == I2C_COUNT) begin
                nstate  = S_IDLE;      // 接收完ACK,转向空闲
            end            
            else begin
                nstate  = S_WAIT_ACK; 
            end
        end
        
        // 接收数据
        S_RECV_DATA: begin
            if(bits == 4'd8) begin
                nstate  = S_SEND_ACK;  // 接收完数据,发送ACK/NAK
            end            
            else begin
                nstate  = S_RECV_DATA; 
            end
        end 
        // 主机响应
        S_SEND_ACK: begin
            if(i2c_cnt == I2C_COUNT) begin
                nstate  = S_IDLE;      // 发送完ACK/NAK,转向空闲
            end            
            else begin
                nstate  = S_SEND_ACK; 
            end
        end
        
        // 发送停止位
        S_STOP: begin
            if(i2c_cnt == I2C_COUNT) begin
                nstate  = S_IDLE;      // 发送完停止位,转向空闲
            end            
            else begin
                nstate  = S_STOP; 
            end
        end 
        
        default: begin
            nstate = S_IDLE;
        end    
    endcase
end

//-----------------------------------------
// 三段状态机第三段(输出)
always @(posedge clk or negedge rst_n) begin

    if(~rst_n) begin
        isout <= 0;     // 设置双向端口为输入
        sda_r <= 1;     // 复位时,设置为高电平
        scl_r <= 1;     // 复位时,设置为高电平
    end 
    
    else begin
        case(cstate)
        S_IDLE: begin
            done <= 0; 
            bits <= 0; 
        end
        
        // 发送起始位:SCL为高时,SDA下跳
        // 前置状态:SCL为高,SDL为高(S_IDLE);
        // 后续状态:SCL为低,SDA为低(S_SEND_DATA);
        S_START: begin   
            if(i2c_cnt == 20'd100) begin
                scl_r <= 1'b1;      // SCL拉高
            end            
            else if(i2c_cnt == 20'd200) begin
                sda_r <= 1'b0;      // SDA下跳(产生起始位)
            end            
            else if(i2c_cnt == 20'd400) begin
                scl_r <= 1'b0;      // SCL拉低为次数据做准备     
            end                            
            else begin         
                isout <= 1'b1;      // 设置为输出   
            end   
        end        

        // 发送数据:SCL低电平准备,SCL上跳送出
        // 前置状态:SCL为低(重要)(S_START/S_WAIT_ACK);
        // 后续状态:SCL为低,SDAl输入(S_WAIT_ACK)
        S_SEND_DATA: begin           
            if(bits == 4'd8) begin
                isout <= 0;    // 设置为输入准备接收ACK
                sda_r <= 1;                        
            end            
            else if(i2c_cnt == 20'd100) begin
                isout <= 1'b1;              // 设置为输出  
                sda_r <= wdata[7 - bits];   // 低电平时准备SDA数据
            end        
            else if(i2c_cnt == 20'd200) begin
                scl_r <= 1'b1;              // SCL上跳发送数据 
            end            
            else if(i2c_cnt == 20'd400) begin
                scl_r <= 1'b0;              // SCL拉低准备下次发送   
            end   
            else if(i2c_cnt == I2C_COUNT) begin                 
                bits <= bits + 1'b1;        // 时序结束时才计数
            end 
        end 
        
        // 等待ACK: 由设备回应,设备在SCL拉低之后一段时间内已经准备好了数据;
        // 前置状态:SCL为低,SDA输入(S_SEND_DATA)
        // 后续状态:SCL为低,SDA输入
        S_WAIT_ACK: begin        
            if(i2c_cnt == 20'd200) begin
                isout <= 0;    // 设置为输入准备接收ACK
                scl_r <= 1'b1; // SCL上跳以接收ACK                
            end             
            if(i2c_cnt == 20'd400) begin
                scl_r <= 1'b0; // SCL拉低以准备下次发送 
                // 在此处理ACK
            end    
            else if(i2c_cnt == I2C_COUNT) begin 
                bits <= 0;
                done <= 1;
            end   
            else begin
                isout <= 1'b0;     // 设置为输入 
            end
        end       
        
        // 接收数据:SCL低电平准备 
        // 前置状态:SCL为低,SDA输入(S_WAIT_ACK)
        // 后续状态:SCL为低,SDA输入(S_SEND_ACK)
        S_RECV_DATA: begin           
            if(bits == 4'd8) begin 
                bits  <= 0;
            end        
            else if(i2c_cnt == 20'd100) begin
                scl_r <= 1'b0;                  // SCL先拉低,因为不确定之前的电平
            end
            else if(i2c_cnt == 20'd200) begin
                scl_r <= 1'b1;                  // SCL上跳接收数据 
            end            
            else if(i2c_cnt == 20'd300) begin
                rdata_r[7 - bits] <= sda;       // 数据稳定后读取数据
            end               
            else if(i2c_cnt == 20'd400) begin
                scl_r <= 1'b0;                  // SCL拉低准备下次发送  
            end  
            else if(i2c_cnt == I2C_COUNT) begin
                bits <= bits + 1'b1;
            end           
            else begin         
                isout <= 1'b0;                  // 设置为输入   
            end   
        end
        
        // 发送ACK: ACK(0)/NAK(1)
        // 前置状态:SCL为低,SDA输入,
        // 后续状态:SCL为高,SDA为低(STOP)
        S_SEND_ACK: begin        
            if(i2c_cnt == 20'd100) begin
                isout <= 1;                     // 设置为输出
                sda_r <= 1'b1;                  // SCL低电平设置ACK(0)/NAK(1)数据
            end 
            else if(i2c_cnt == 20'd200) begin                
                scl_r <= 1'b1;                  // SCL上跳发送
            end 
            else if(i2c_cnt == 20'd400) begin
                scl_r <= 1'b0;                 // SCL拉低以准备修改SDA    
            end         
            else if(i2c_cnt == I2C_COUNT) begin
                sda_r <= 0;                    // 恢复为低电平(准备上跳停止)
                done  <= 1;
            end 
            else begin
            
            end
        end         
            
        // 发送停止位: SCL为高电平时,SDA上跳
        // 前置状态:SCL为低,SDA不确定
        // 后续状态:SCL为高,SDA为高(准备下次起始位)
        S_STOP: begin          
            if(i2c_cnt == 20'd200) begin
                isout <= 1'b1;      // 输出
                sda_r <= 1'b0;      // 设置SDA输出低电平
            end
            else if(i2c_cnt == 20'd300) begin
                scl_r   <= 1'b1;    // SCL设置为高电平 
            end
            else if(i2c_cnt == 20'd400) begin
                sda_r   <= 1'b1;    // SDA上跳  
            end
            else if(i2c_cnt == I2C_COUNT) begin        
                done <= 1;
            end
            else begin         
                isout <= 1'b1; 
            end  
        end    
        default:  begin
            done <= 0;
        end                
        endcase            
    end        
end

endmodule

I2C模块接口图

- 阅读全文 -


Copyright©2025 春天花会开, All Rights Reserved. Email: webmaster@oroct.com