本帖最后由 如意不如意 于 2020-8-4 16:41 编辑
例程二十二:【MicroPython】RAM-FPGA I2C通信
一、实验目的与意义
了解I2C总线通信的基本原理;掌握RAM与FPGA的I2C通信方法;掌握Quartus II集成环境使用方法。
二、实验设备及平台- iCore3双核心板点击购买
- Micro USB线
- Blaster(或相同功能)仿真器
- Quartus II开发平台
- 串口调试器
- 电脑
三、实验原理
通过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核心板已经将以上引脚相连。
四、实验步骤- 用数据线将iCore3的USB-OTG口与电脑相连,将供电跳帽选择为OTG供电;
- 将代码包中的custom_i2c.py、main.py文件替换到PYBFLASH磁盘中,弹出磁盘(必须!),拔下数据线将iCore3断电;
- 将USB-Blaster与iCore3的JTAG调试口相连;
- 将跳线帽选为USB UART,并将数据线连接到USB UART;
- 将数据线连接到电脑,iCore3上电Python代码运行;
- 打开QuartusII开发环境,打开实验工程,编译工程,下载FPGA代码;
- 打开串口调试器,连接到对应串口并打开,发送命令,观察实验现象;
- 命令格式如下:
命令字符串 | RAM-LED | FPGA-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) # 返回接收到的字符串
代码包.zip
(1.43 MB, 下载次数: 1)
|