银杏科技有限公司旗下技术文档发布平台 | |||
技术支持电话 | 0379-69926675-801 | ||
技术支持邮件 | Gingko@vip.163.com | ||
版本 | 日期 | 作者 | 修改内容 |
---|---|---|---|
V1.0 | 2020-07-08 | gingko | 初次建立 |
了解I2C总线通信的基本原理;掌握RAM与FPGA的I2C通信方法;掌握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核心板已经将以上引脚相连。
命令字符串 | RAM-LED | FPGA-LED |
ledr | 红灯亮 | 红灯亮 |
ledg | 绿灯亮 | 绿灯亮 |
ledb | 蓝灯亮 | 蓝灯亮 |
# -*- 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) # 返回接收到的字符串