当前位置: 首页 > news >正文

STM32H750寄存器操作(硬件I2C)

1.前言

最近使用的屏幕触控IC是GT911,这不巧了吗前面用另一款3.5寸tft也是这个IC,本来我是不想写I2C的但是拿着例程跑一下去实验,实在是蚌埠住了。首先是程序用的软件模拟I2C,其次程序还在主函数里加了20ms的延迟,程序里说是:触摸屏扫描间隔不能小于10ms,建议设置为20ms。我真是服了,最后我试了一下实际效果只能说真卡。实际效果甚至不如我用STC32G主频30Mhz跑起来的效果流畅。最后没办法,我想了想还是得修改硬件把硬件I2C跑起来。

说回H750的I2C,我实际使用起来,它的硬件结构与F4,F1系列有非常大的不同。F1,F4的I2C硬件构成是一摸一样的。而到了H7,ST官方几乎是把I2C重新设计了,不过官方手册写的非常完善了,把整个I2C从初始化到发生接受单个和多个字节的流程都讲了一遍。写起来难度其实不怎么大。

2.理论

2.1 时钟配置

首先我们看看手册上对初始化的描述

首先要配置I2C的时钟,这块是整个程序中最复杂的部分,我就简单介绍一下吧。首先我们的I2C时钟有4个来源,默认状态下使用APB1总线的时钟。那么我们的时钟源是120Mhz,在I2C内部有一个分频器可以再次分频。之后我们按照公式计算出SCL高低电平的长度即可控制周期了,手册上给出了经典的数值,我这里带大家过一下如何计算。

以100Khz为例,我们主时钟频率为48Mhz,我们的分频系数是0xb即11,这里注意一下我们投入的所有数值都是要+1才是实际值,那么实际分频系数是12,我们最后I2C频率为4Mhz,一个周期为250ns。然后配置低电平持续0x13即19即20个周期,也就是20x250ns=5us,这就是这一栏5us的来源。

同理计算高电平持续时间0xf即15即维持16个周期,也就是16x250=4us。具体的计算方式和原因还是比较复杂的,有兴趣的读者可以翻阅数据手册,要是大家想简单搞就用cubemx配置即可。

2.2 发送地址

首先的最开始发送地址阶段我们要配置地址,选择7位或10位,之后是配置传输方向和发送的字节数,配置完成后启动就可以了。

2.3 发送数据

发送过程手册上给出了文字描述和流程图,我们来具体看一下。

首先是初始化I2C,没啥好说的,之后我们要配置AutoEND

通常通过I2C写入寄存器会有restart信号,那么我们需要将Autoend置零;否则单纯写入连续写入给Autoend置一即可,置一之后在发送完最后一位数据后芯片会自动发送stop信号。然后按顺序配置好地址,给start置位这一串数据就可以正常发出去了。

再往下我们分为了两条路,左边这一侧是通讯异常状态,即地址不匹配或是没有收到ACK信号,我们就会进入这一分支,芯片会自动结束通讯。

我们重点看一看右侧,这一侧是我们正常的通讯流程

当我们地址发送出去后,我们需要等待状态寄存器的TXIS置位,这一位是啥?

这一位是表示数据已经发送出去,TXDR寄存器的数据正常发送了。

数据发送出去后,我们可以再次对TXDR寄存器进行写入。如果我们没有把所有数据全部发送,则循环这一过程。

当我们所有数据全部发送出去了

由于我们开启了Autoend,流程会自动发送停止位。

2.4 接受数据

过程比较类似,注意好传输方向即可,具体后面我结合具体程序来介绍

3.寄存器介绍

整个I2C寄存器比较少,我挑几个来介绍一下

首先是CR1寄存器的第0,7位

第7位是错误中断,大家如果需要可以开启,防止I2C进入错误情况。然后是第0位,使能外设,不多讲了。

然后是CR2寄存器,基本所有位都要用到

首先是0~9位是地址位,这里要格外注意,我们的七位地址是1~7位

第10位是读写方向,按照实际需要配置即可

第11位是寻址模式,正常我们是7位寻址模式

13、14位是起始和停止位生成,置1发送对应的信号,这两个我们一会是要用到的信号,其他的倒没有。

然后是16~23位是字节数,这就是我们一个通讯流程中需要发送的字节数目,正常写入即可。

之后是25位,当Autoend置0时SCL会延长并等待我们重新发送起始位或停止位。而当Autoend置1时发送完数据会自动发送停止位。

然后我们看看timg寄存器,按照前文提到的方式进行配置,注意位置和+1的情况,其他的没啥特别的地。

然后我们看看ISR寄存器

其中1,2位是我们用的最多的,是关于发送和接受的状态,详细的可以看看手册的描述

然后是5、6位,第6位是传输完成标志位。当TC=1意味着我们这一流程发送完毕可以发送停止位了,这一位我们用的也比较多。

第5位则是因为由于我们会使用Autoend,所以通过这一位可以判断我们所有过程是否全部结束,MCU对于总线的控制权是否释放。

顺带我们看一下数据寄存器,H7有一个典型特点就是数据寄存器分离了,发送就是单独的32位数据寄存器,接收也是单独的寄存器,这一点还是相当不错的。

4.程序

程序我先放上来

#include "i2c1.h"/*
注意,I2C一定要加上超时的设置,否则当IIC总线出错时,
没有超时检测可能造成MCU卡死在这里。
*/
void init_i2c1(void)
{RCC->APB1LENR|=1<<21;		//使能时钟I2C1->CR1&=~(1<<0);			//关闭I2C通信准备配置//初始化端口GPIOB->MODER&=~(3<<16);GPIOB->MODER&=~(3<<18);GPIOB->MODER|=2<<16;GPIOB->MODER|=2<<18;GPIOB->OSPEEDR|=1<<16;GPIOB->OSPEEDR|=1<<18;//!!!一定要设置成开漏输出模式!!!GPIOB->OTYPER|=1<<8;GPIOB->OTYPER|=1<<9;GPIOB->AFR[1]|=4<<0;GPIOB->AFR[1]|=4<<4;//设置I2C主频与通信速率//I2C挂在APB1上频率为120MhzI2C1->TIMINGR=0;I2C1->TIMINGR|=3<<28;	//设置预分配系数为4 分频后时钟频率为30Mhz#if I2C_SPEED==100//H:5us+L:5usI2C1->TIMINGR|=144<<0;//SCLLI2C1->TIMINGR|=144<<8;//SCLHI2C1->TIMINGR|=6<<16;	//SDADEL=2I2C1->TIMINGR|=1<<20;	//SCLDEL=1#elif I2C_SPEED==400//H:1us+L:1.5usI2C1->TIMINGR|=37<<0;//SCLLI2C1->TIMINGR|=25<<8;//SCLH//(SDADEL + 1)*33.333 ns >100 nsI2C1->TIMINGR|=3<<16;	//SDADEL=2I2C1->TIMINGR|=1<<20;	//SCLDEL=1#elif I2C_SPEED==1000//H:0.4us+L:0.6usI2C1->TIMINGR|=13<<0;//SCLLI2C1->TIMINGR|=5<<8;//SCLH//(SDADEL + 1)*33.333 ns > 50 nsI2C1->TIMINGR|=2<<16;	//SDADEL=1I2C1->TIMINGR|=1<<20;	//SCLDEL=0#endifI2C1->CR2&=~(1<<11);	//7位地址模式I2C1->CR1|=1<<7;			//开启错误中断I2C1->CR1|=1<<0;	//开启I2C通讯
}void i2c1_WriteOneChar(unsigned char i2c_address,unsigned char i2cin)
{I2C1->CR2&=~(0xff<<16);//清空字节长度I2C1->CR2|=1<<16;//写入字节长度I2C1->CR2|=1<<25;			//所有字节传输完毕后发送stop信号I2C1->CR2&=~(0x3FF);	//清空地址信息I2C1->CR2|=i2c_address<<1;//写入地址I2C1->CR2&=~(1<<10);		//主模式为写方向I2C1->CR2|=1<<13;			//发送起始信号while((I2C1->ISR&(1<<1))==0);//等待发送缓冲区为空I2C1->TXDR=i2cin;			//写入要传输的数据while((I2C1->ISR&(1<<5))==0);//等待停止位发送完毕
}unsigned char i2c1_ReadOneChar(unsigned char i2c_address)
{unsigned char i2creadtemp;I2C1->CR2|=1<<16;			//写入一个字节I2C1->CR2|=1<<25;			//所有字节传输完毕后发送stop信号I2C1->CR2&=~(0x3FF);	//清空地址信息I2C1->CR2|=i2c_address<<1;//写入地址I2C1->CR2|=1<<10;			//主模式为读方向I2C1->CR2|=1<<13;			//发送起始信号while((I2C1->ISR&(1<<2))==0);//等待接收缓冲区为空i2creadtemp=I2C1->RXDR;while((I2C1->ISR&(1<<6))==0);//等待所有数据数据传输完毕return i2creadtemp;
}void i2c1_MultiWrite(unsigned char i2c_address,unsigned char* i2cdata,unsigned char length)
{unsigned char i2cwei=length;I2C1->CR2&=~(0xff<<16);//清空字节长度I2C1->CR2|=length<<16;//写入字节长度I2C1->CR2|=1<<25;			//所有字节传输完毕后发送stop信号I2C1->CR2&=~(0x3FF);	//清空地址信息I2C1->CR2|=i2c_address<<1;//写入地址I2C1->CR2&=~(1<<10);		//主模式为写方向I2C1->CR2|=1<<13;			//发送起始信号for(i2cwei=0;i2cwei<length;i2cwei++){while((I2C1->ISR&(1<<1))==0);//等待发送缓冲区为空I2C1->TXDR=*(i2cdata+i2cwei);			//写入要传输的数据}while((I2C1->ISR&(1<<5))==0);//等待停止位发送完毕
}void i2c1_MultiRead(unsigned char i2c_address,unsigned char* i2cdata,unsigned char length)
{unsigned char i2cwei;I2C1->CR2|=length<<16;			//写入字节数I2C1->CR2|=1<<25;			//所有字节传输完毕后发送stop信号I2C1->CR2&=~(0x3FF);	//清空地址信息I2C1->CR2|=i2c_address<<1;//写入地址I2C1->CR2|=1<<10;			//主模式为读方向I2C1->CR2|=1<<13;			//发送起始信号for(i2cwei=0;i2cwei<length;i2cwei++){while((I2C1->ISR&(1<<2))==0);//等待接收缓冲区为空i2cdata[i2cwei]=I2C1->RXDR;}while((I2C1->ISR&(1<<6))==0);//等待所有数据数据传输完毕
}void i2c1_WriteRegist(unsigned char i2c_address,unsigned char *regaddress,unsigned char registlength,unsigned char* i2cdata,unsigned char datalength)
{unsigned char i2cwei;I2C1->CR2&=~(0xff<<16);//清空字节长度I2C1->CR2|=registlength<<16;//写入字节长度I2C1->CR2&=~(1<<25);			//所有字节传输完毕后发送restart信号I2C1->CR2&=~(0x3FF);	//清空地址信息I2C1->CR2|=i2c_address<<1;//写入地址I2C1->CR2&=~(1<<10);		//主模式为写方向I2C1->CR2|=1<<13;			//发送起始信号for(i2cwei=0;i2cwei<registlength;i2cwei++){while((I2C1->ISR&(1<<1))==0);//等待发送缓冲区为空I2C1->TXDR=*(regaddress+i2cwei);			//写入要传输的数据}I2C1->CR2&=~(0xff<<16);//清空字节长度I2C1->CR2|=datalength<<16;//写入字节长度I2C1->CR2&=~(0x3FF);	//清空地址信息I2C1->CR2|=i2c_address<<1;//写入地址I2C1->CR2&=~(1<<10);		//主模式为写方向I2C1->CR2|=1<<13;			//发送起始信号for(i2cwei=0;i2cwei<datalength;i2cwei++){while((I2C1->ISR&(1<<1))==0);//等待发送缓冲区为空I2C1->TXDR=*(i2cdata+i2cwei);			//写入要传输的数据}while((I2C1->ISR&(1<<6))==0);//等待所有数据传输完毕I2C1->CR2|=1<<14;	//发送停止位while((I2C1->ISR&(1<<5))==0);//等待停止位发送完毕
}void i2c1_ReadRegist(unsigned char i2c_address,unsigned char *regaddress,unsigned char registlength,unsigned char* i2cdata,unsigned char datalength)
{unsigned char i2cwei;I2C1->CR2&=~(0xff<<16);//清空字节长度I2C1->CR2|=registlength<<16;//写入字节长度I2C1->CR2&=~(1<<25);			//所有字节传输完毕后发送restart信号I2C1->CR2&=~(0x3FF);	//清空地址信息I2C1->CR2|=i2c_address<<1;//写入地址I2C1->CR2&=~(1<<10);		//主模式为写方向I2C1->CR2|=1<<13;			//发送起始信号for(i2cwei=0;i2cwei<registlength;i2cwei++){while((I2C1->ISR&(1<<1))==0);//等待发送缓冲区为空I2C1->TXDR=*(regaddress+i2cwei);			//写入要传输的数据}while((I2C1->ISR&(1<<6))==0);//等待所有数据数据传输完毕I2C1->CR2&=~(0xff<<16);//清空字节长度I2C1->CR2|=datalength<<16;			//写入字节数I2C1->CR2&=~(0x3FF);	//清空地址信息I2C1->CR2|=i2c_address<<1;//写入地址I2C1->CR2|=1<<10;			//主模式为读方向I2C1->CR2|=1<<13;			//发送起始信号for(i2cwei=0;i2cwei<datalength;i2cwei++){while((I2C1->ISR&(1<<2))==0);//等待接收缓冲区为空i2cdata[i2cwei]=I2C1->RXDR;}while((I2C1->ISR&(1<<6))==0);//等待所有数据数据传输完毕I2C1->CR2|=1<<14;	//发送停止位while((I2C1->ISR&(1<<5))==0);//等待停止位发送完毕
}

首先是初始化,把时钟开启和配置管脚,这个不多说了

之后是配置通讯速率,我这里用的是4分频,数据是我通过计算配合实验得出来的。

最后是配置好地址模式并开启I2C

然后是发送过程,过程几乎和手册上描述的一致,配置好字节长度和地址信息。由于是多字节写入,所以我们使用的是Autoend模式,发送完最后一个字节自动发送停止位。然后注意好传输方向就可以了。

之后我们具体的发送过程我们按照手册要求,检测发送缓冲区,为空后依次写入数据即可。

由于我们是自动发送停止位,所以我们要检测停止位是否发送出去了。

我们再看一下读取过程,过程非常类似,注意好基础配置即可

然后我们再看看读寄存器流程,这是最复杂的过程。前半段没什么区别,主要在这里Autoend要为0,发送完这一段数据后,我们要准备发送restart信号。

后半段我们要注意两点,其一是重新配置方向,设置为读取方向。其二是由于我们没有开启Autoend模式,所以停止位需要由我们自己主动发送。

5.测试

首先我们先来测试连续发送,这里我先发送四个字节

是随便写的四个字节,从机地址为0x11

波形如下

速率为400Khz

然后是读取

6.结语

最开始开发的时候,我以为H7的I2C结构和F4非常相似,但是很显然ST官方把I2C重构了,以往令人诟病的bug基本算消失了。手册上写的也非常全面了,按照手册上的时序正常配置基本没有什么问题,官方的流程图给的也非常清晰了,踩坑也比较少了,这一点还是要给ST官方点赞的。那么OK,还是老样子,我们下一篇文章见。

http://www.dtcms.com/a/524657.html

相关文章:

  • 算法18.0
  • RHCA - DO374 | Day02:管理内容集和执行环境
  • 网站建设明细价格表包头seo营销公司
  • JAVA 锁机制【待完善】
  • 不平均的分治——根号分治
  • USP-(DeepSpeed-Ulysses-Attention and Ring-Attention)
  • Ubuntu部署集群环境(3台)
  • VoCo-LLaMA: Towards Vision Compression with Large Language Models 译读笔记
  • 国网北京电力建设研究院网站惠州网站建设电话
  • 鹤壁市住房和城乡建设局网站上线了建站教程
  • centos8.5运行ai00-server报错`GLIBC_2.39‘ not found,解决方法
  • 冷换仓的隐性代价:从安全策略到地址信誉体系的重新思考
  • 如何用Vue CLI 创建 Vue 项目
  • 网站开发专业前景完整php网站开发
  • 企业建网站110平米三室一厅简装图片
  • CAS汽车固件签名:从“完成签名”到“安全治理”的演进之路
  • 免费手机网站模板sem竞价推广怎么做
  • 企业数字化转型的关键一步:打通研发全流程
  • Unity 资源导出的问题,依赖关系过多。
  • 网站开发公司内部数据字典深圳网站建设设计公司
  • 网站建设维护公司地址如何做好网站内容
  • STM32_bug总结-运行函数在SystemInit之后就卡死
  • 基于高光谱成像技术的烟叶含水率检测研究进展
  • 聊城网站推广网站可以换域名吗
  • 一个网站 两个域名淄博公司网站建设价格
  • VBA数据结构终极对决:性能实测与行业应用指南
  • 解码Linux文件IO之JPEG图像原理与应用
  • “短小精悍”的边缘AI算力利器:超微SYS-E403-14B-FRN2T服务器评测
  • Gradio全解14——使用Gradio构建MCP的服务器与客户端(4)——Python包命令:uv与uvx实战
  • php是网站开发的语言吗怎么做好市场宣传和推广