用户工具

站点工具


icore3_micropython_22
银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
V1.0 2020-07-08 gingko 初次建立

例程二十二:【MicroPython】RAM-FPGA I2C通信

一、实验目的与意义

了解I2C总线通信的基本原理;掌握RAM与FPGA的I2C通信方法;掌握Quartus II集成环境使用方法。

二、实验设备及平台

  1. iCore3双核心板点击购买
  2. Micro USB线
  3. Blaster(或相同功能)仿真器
  4. Quartus II开发平台
  5. 串口调试器
  6. 电脑

三、实验原理

通过FPGA建立的I2C模块对外提供SCL、SDA接口,与STM32模拟的I2C接口相连接,串口调试器与STM32通过串口连接,实现三者之间的通信。本实验中,以STM32作为主机,FPGA作为从机,串口调试器向STM32发送数据,STM32的串口4接收数据,然后通过I2C将数据发送至FPGA,STM32起到一个桥梁的作用。程序运行后,FPGA收到数据后向STM32发送数据,经过STM32发送至串口调试器。原理示意图如下所示: 实验通讯示意图

排针引脚连接对照表:

SPI信号线 ARM引脚 FPGA引脚
时钟信号(SCL) PD8 D8
数据信号(SDA) PD9 F7

注意:iCore3核心板已经将以上引脚相连。

四、实验步骤

  1. 用数据线将iCore3的USB-OTG口与电脑相连,将供电跳帽选择为OTG供电;
  2. 将代码包中的custom_i2c.py、main.py文件替换到PYBFLASH磁盘中,弹出磁盘(必须!),拔下数据线将iCore3断电;
  3. 将USB-Blaster与iCore3的JTAG调试口相连;
  4. 将跳线帽选为USB UART,并将数据线连接到USB UART;
  5. 将数据线连接到电脑,iCore3上电Python代码运行;
  6. 打开QuartusII开发环境,打开实验工程,编译工程,下载FPGA代码;
  7. 打开串口调试器,连接到对应串口并打开,发送命令,观察实验现象;
  8. 命令格式如下:
命令字符串RAM-LEDFPGA-LED
ledr 红灯亮 红灯亮
ledg 绿灯亮 绿灯亮
ledb 蓝灯亮 蓝灯亮

五、实验现象

在发送区编辑完要发送的数据后,点击发送即可观察到RAM-LED、FPGA-LED变化,并在接收区收到iCore3的回复信息,如下图所示: 串口调试器界面

六、实验程序

# -*- coding: utf-8-*-
# main.py -- put your code here!
from pyb import Pin, udelay, LED, UART  # 导入相关模块
from custom_i2c import I2C  # 从定制i2c模块中导入I2C对象
 
# 主函数
# 定义串口4对象,波特率9600
U4 = UART(4,9600)
 
# 定义I2C对象,定义 sda:D9, scl:D8 (iCore3硬件连接)
fpga_i2c = I2C(Pin('D9'), Pin('D8'))
 
while True:
    re = U4.readline()           # 缓冲区读取一行内容
    if re != None:               # 若内容不为空
        udelay(5000)             # 延时5ms,等内容接受完整,视内容长短而定,若时间过短则会通信失败
        re = re + U4.readline()  # 将内容合并
 
        if re == b'ledr':        # 控制命令匹配
            LED(1).on()          # 红灯亮
            LED(2).off()
            LED(3).off()
        elif re == b'ledg':      # 绿灯亮
            LED(1).off()
            LED(2).on()
            LED(3).off()
        elif re == b'ledb':      # 蓝灯亮
            LED(1).off()
            LED(2).off()
            LED(3).on()
 
        # 将命令发送给FPAG
        data = re.decode('ascii')+'\r\n'
        fpga_i2c.write_nbyte(0x03, 0X8F, data)
        res = fpga_i2c.read_nbyte(0x02,0x0F,0x06)      # 再读入六字节
        U4.write(res)                 # 将从FPGA接收到的内容再发给串口4
# -*- coding: utf-8-*-
# custom_i2c.py -- put your code here!
from pyb import Pin, udelay   # 导入相关模块
 
# 普通IO模拟I2C驱动
class I2C(object):
    """创建I2C对象时传入两个参数
        sda:   数据信号
        scl:   时钟信号
    """
    # 对象创建时,自动初始化
    def __init__(self, sda, scl):
        super(I2C, self).__init__()
        self.sdapin = sda                           # sda引脚编号
        self.sclpin = scl                           # scl引脚编号
        self.I2C_SDA = Pin(self.sdapin, Pin.OUT_PP) # 数据信号线输出
        self.I2C_SCL = Pin(self.sclpin, Pin.OUT_PP) # 时钟信号线输出
        self.I2C_SDA.high()        # SDA拉高
        self.I2C_SCL.high()        # 时钟拉高
        self.i2c_flag = 0          # 应答标志
        self.us = 10               # 信号稳定延时
 
    # 起始信号
    def start(self):
        self.I2C_SDA.high()
        udelay(self.us)
        self.I2C_SCL.high()
        udelay(self.us)
        self.I2C_SDA.low()
        udelay(self.us)
        self.I2C_SCL.low()
        udelay(self.us)
 
    # 停止信号
    def stop(self):
        self.I2C_SDA = Pin(self.sdapin, Pin.OUT_PP)
        self.I2C_SDA.low()
        udelay(self.us)
        self.I2C_SCL.high()
        udelay(self.us)
        self.I2C_SDA.high()
        udelay(self.us)
 
    # 应答信号
    def ack(self, char):
        self.I2C_SDA = Pin(self.sdapin, Pin.OUT_PP)
        if char:                   # 若成功读取数据,则将数据线拉低
            self.I2C_SDA.low()
        else:
            self.I2C_SDA.high()
        udelay(self.us)
 
        self.I2C_SCL.high()
        udelay(self.us)
        self.I2C_SCL.low()
        udelay(self.us)
 
    # 写一字节
    def write(self, data):
        self.I2C_SDA = Pin(self.sdapin, Pin.OUT_PP)
        for i in range(8):           # 循环八次,发送一字节
            if data & 0x80:          # 发送最高位的一比特
                self.I2C_SDA.high()  # 数据信号线拉高
            else:
                self.I2C_SDA.low()   # 数据信号线拉低
 
            udelay(self.us)
            self.I2C_SCL.high()
            udelay(self.us)
            self.I2C_SCL.low()
            data <<= 1               # 数据位左移一位
 
        # 再第九个时钟周期,从机回复一个ACK信号,主机将数据线设置为输入模式
        self.I2C_SDA = Pin(self.sdapin, Pin.IN, Pin.PULL_UP)
        udelay(self.us)
        udelay(self.us)
        self.I2C_SCL.high()
        udelay(self.us)
 
        if self.I2C_SDA.value():    # 若SDA没有被拉低,则代表接受异常
            self.i2c_flag = 0     
        else:
            self.i2c_flag = 1       # SDA被拉低代表接受正常,将i2c_flag赋值为1
 
        self.I2C_SCL.low()          # 主机将数据线设置为输出模式
        self.I2C_SDA = Pin(self.sdapin, Pin.OUT_PP)
        udelay(self.us)
 
    # 发送n字节,参数为sla:设备地址,suba:寄存器地址,s:字符串
    def write_nbyte(self, sla, suba, s):
        self.I2C_SDA = Pin(self.sdapin, Pin.OUT_PP)
        self.start()                # 发送起始信号
        self.write(sla)             # 发送设备地址
        self.write(suba)            # 发送寄存器地址
        for i in range(len(s)):     # 逐个发送字符
            self.write(ord(s[i]))   # 将字符转换位ascii码值
        self.stop()                 # 发送停止信号
 
        return(1)
 
    # 读一字节
    def read(self):
        retc = 0
        self.I2C_SDA.high()
        self.I2C_SDA = Pin(self.sdapin, Pin.IN, Pin.PULL_UP)
 
        for i in range(8):
            udelay(self.us)
            self.I2C_SCL.low()
            udelay(self.us)
            self.I2C_SCL.high()
            udelay(self.us)
            retc <<= 1
            if self.I2C_SDA.value(): # 读取输入信号线
                retc += 1            # 接收变量按位赋值
            udelay(self.us)
 
        self.I2C_SCL.low()
        udelay(self.us)
        self.I2C_SDA = Pin(self.sdapin, Pin.OUT_PP)
 
        return(chr(retc))            # 将ascii码转换为字符
 
    # 读取n字节,参数为sla:设备地址,suba:寄存器地址,no:字节长度
    def read_nbyte(self, sla, suba, no):
        re = ''
        self.start()                 # 发送起始信号
        self.write(sla)              # 发送设备地址
        self.write(suba)             # 发送寄存器地址
 
        for i in range(no-1):
            re += self.read()
            self.ack(1)              # 发送应答信号
        re += self.read()
        self.ack(0)
        self.stop()                  # 发送停止信号
 
        return(re)                   # 返回接收到的字符串
icore3_micropython_22.txt · 最后更改: 2022/03/18 15:37 由 sean