历史上的今天
返回首页

历史上的今天

今天是:2025年04月11日(星期五)

正在发生

2019年04月11日 | 写STM32 的I2c库函数

2019-04-11 来源:eefocus

I2c协议:

 I2c是一种双向串行通讯标准,常用于嵌入式系统中。利用I2c总线可以利用有限的I/O接口来扩展多功能的外围设备。主要由SCL(时钟线)和SDA(数据线组成)。I2c总线上可以连接多个带有I2c接口的设备,每个设备都有自己唯一的地址。设备地址一般看该设备对应的手册。当总线空闲的时候SDA线和SCL线都为高电平,如果SCL处于高电平时SDL产生下降沿则认为起始位,如果SCL处于高电平SDA产生上升沿时则为停止位。



主发送从接收:

主要讲的是Stm32配置I2c协议成主发送从接收模式,我们之前看到的都是调用STM32的I2c的官方库函数来配置I2c,今天呢我们是自己配置寄存器来写一个I2c的库函数。


第一步:开启时钟外设看STM32对应的手册来配置相应的寄存器。我写的是STM32f303所以看对应手册开启时钟外设寄存器为RRC_APB1ENR寄存器。再次强调对应的芯片不同手册里面要配置的寄存器也不一样,一切要按照对应的手册写,这里只是提供一个参考。


///Reinitialize the I2C peripheral registers to their default reset values

void I2c::Initialize(uint8_t channel)

{

base = ( I2C_TypeDef * ) ( I2C1_BASE + ( ( channel - 1 ) * baseRegOffset ) );

 

if (_I2c1 == channel)

{

// I2C 1 clock enable   

RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;

}

else if (_I2c2 == channel)

{

//I2C 2 clock enable

RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;

}

}


第二步:配置时钟频率(一般模式,快速模式,高速模式)。主要配置以下几个寄存器。把以下几个寄存器配置好了I2c的时序也相应的会产生。


///set I2C master  Clock frequency

void I2c::ConfigClk(uint8_t SCLL, uint8_t SCLH, uint8_t PECSC, uint8_t SDADEL, uint8_t SCLDEL)

{

Disable();

//I2CCLK timing setting.

base->TIMINGR &= ~I2C_TIMINGR_SCLL_Msk;

base->TIMINGR &= ~I2C_TIMINGR_SCLH_Msk;

base->TIMINGR &= ~I2C_TIMINGR_PRESC_Msk;

base->TIMINGR &= ~I2C_TIMINGR_SDADEL_Msk;

base->TIMINGR &= ~I2C_TIMINGR_SCLDEL_Msk;

 

base->TIMINGR |= SCLL << I2C_TIMINGR_SCLL_Pos;

base->TIMINGR |= SCLH << I2C_TIMINGR_SCLH_Pos;

base->TIMINGR |= PECSC << I2C_TIMINGR_PRESC_Pos;

base->TIMINGR |= SDADEL << I2C_TIMINGR_SDADEL_Pos;

base->TIMINGR |= SCLDEL << I2C_TIMINGR_SCLDEL_Pos;

Enable();

}


设置频率时我这里给了一个例子可以参考,往相应的寄存器输入值,这个值也是在手册里面找的。不能自己乱写哈。


///Set clockspeed

///Mode of timings settings

///Standard mode  Fast-mode  Fast-mode Plus 

void I2c::I2cClockSpeed(uint8_t speed)

{

//Standard mode100 kHz

if (100 == speed)

{

ConfigClk(0x13, 0xf, 1, 0x2, 0x4);

}

 

//Fastmode mode 400kHz

if (400 == speed)

{

ConfigClk(0x9, 0x3, 0, 0x1, 0x3);

}

 

//Fastmode Plus

if (500 == speed)

{

ConfigClk(0x6, 0x3, 0, 0x0, 0x01);

}

}

      

第三步:初始化对应的Gpio引脚,在这里要注意的是一般I2c都配置成开漏输出就可以了。


///Reinitialize the I2C peripheral registers to their default reset values

///I2Cx: where x can be 1, 2 to select the I2C peripheral

void I2c::ConfigPins(I2cPinConfig pinConfig)

{

Gpio sclPin;

Gpio sdaPin;

 

sclPin.Initialize(pinConfig.sclCh, pinConfig.sclPin);

sclPin.ConfigAltFunc(pinConfig.sclAltNum);

sclPin.ConfigMode(Gpio::_Alt);

sclPin.ConfigSpeed(Gpio::_HighSpeed);

 

sdaPin.Initialize(pinConfig.sdaCh, pinConfig.sdaPin);

sdaPin.ConfigAltFunc(pinConfig.sdaAltNum);

sdaPin.ConfigMode(Gpio::_Alt);

sdaPin.ConfigSpeed(Gpio::_HighSpeed);

 

sclPin.ConfigOutputType(Gpio::_OpenDrain);

sdaPin.ConfigOutputType(Gpio::_OpenDrain);

sclPin.ConfigInputType(Gpio::_NoPull);

sdaPin.ConfigInputType(Gpio::_NoPull);

}


第四步:我们可以看对应手册进行读写了。先看I2c作为主模式下是如何写的。我们可以看到写的流程图及产生以下代码:



写过程:



///sends one byte

void I2c::WriteByte(const uint8_t txData, uint8_t address)

{

//Configure slave address and configure direction to write

base->CR2 = ( base->CR2 & ~( I2C_CR2_SADD_Msk | I2C_CR2_RD_WRN ) ) | address;

 

//Set number of bytes to be write

base->CR2 = ( base->CR2 & ~I2C_CR2_NBYTES_Msk ) | ( 1 << I2C_CR2_NBYTES_Pos );

 

//Allow 12C module to send STOP automatically after all bytes are transferred

base->CR2 |= I2C_CR2_AUTOEND;

 

//Send a start condition to begin writing data

base->CR2 |= I2C_CR2_START;

 

while ( !( base->ISR & I2C_ISR_TXIS ) ) {}

base->TXDR = txData;

}


读过程:



///Recevice one byte

uint8_t I2c::ReadByte(uint8_t address)

{

uint8_t rxData;

 

//Configure slave address and configure direction to read

base->CR2 = (base->CR2 & ~I2C_CR2_SADD_Msk) | address | I2C_CR2_RD_WRN;

 

//Set number of bytes to be read

base->CR2 = (base->CR2 & ~I2C_CR2_NBYTES_Msk) | 1 << I2C_CR2_NBYTES_Pos;

 

//Allow 12C module to send STOP automatically after all bytes are transferred

base->CR2 |= I2C_CR2_AUTOEND;

 

//Send a start condition to begin receiving data

base->CR2 |= I2C_CR2_START;

 

while (0 == base->ISR & I2C_ISR_RXNE) {}

rxData = base->RXDR;

 

return rxData;

}


分析一下以上代码,结合上面的流程图就是先确定寻址找到从机地址确定读写方向,设置要读写的字节。我这里


给的例子是读写的一个字节所以我就给了一个1到CR2_NBYTES该寄存器里面。配置一个停止信号、一个start起始


信号和配置一个从机设备地址。有人看到这里可能会问ACK,NACK不要我们自己写吗,是的不用,我们主机只负


责发送一个start信号和一个停止信号给从机设备,从机设备硬件会自己给出应答包。最后我会放一张用逻辑分析仪


分析I2c读写的Eeprom过程图片方便更好理解。

当看到逻辑分析仪scl线和sda线由高电平被拉低就是代表通讯开始,从机接收成功会对应发送ACK。所以ACK应答不需要主机写。配置I2c的库函数到这里感觉一切要以对应的手册为基准。大概我对于I2c的理解就是这些了。下次我们写一下I2c读取Eeprom该注意的地方


推荐阅读

史海拾趣

Avel Lindberg公司的发展小趣事

由于我无法直接获取Avel Lindberg公司的详细发展资料,因此我无法直接为您撰写5个关于该公司发展起来的具体故事。不过,我可以为您提供一个通用的故事框架,您可以根据这个框架结合公司的公开信息来撰写相关故事。

Avel Lindberg公司发展故事框架

  1. 创业初期: 描述Avel Lindberg公司成立的背景,包括创始人或核心团队的愿景、行业趋势以及初始产品或服务。可以提及公司如何克服初创期的困难,如资金短缺、市场竞争激烈等问题,逐渐在市场上站稳脚跟。

  2. 技术突破: 讲述公司在某个关键时期实现的技术突破,这项突破可能是对既有技术的改进,也可能是全新技术的研发。这个突破如何帮助公司打开了新的市场领域,提升了竞争力,成为行业内的领军者。

  3. 合作与扩展: 描述公司如何通过与其他企业、研究机构或高校建立合作关系,来扩大业务范围或提升技术水平。这些合作可能涉及共同研发、市场拓展、品牌建设等方面,对公司的长期发展产生了积极影响。

  4. 国际化战略: 讲述公司如何逐步实施国际化战略,进入海外市场。这包括公司如何分析国际市场的需求和竞争态势,制定合适的市场进入策略,以及在国际市场上取得的成就和面临的挑战。

  5. 社会责任与可持续发展: 介绍公司在发展过程中如何关注社会责任和可持续发展。这可以包括公司在环保、员工福利、社区支持等方面的举措,以及这些举措如何提升了公司的社会形象和品牌价值。

在撰写具体故事时,您可以根据Avel Lindberg公司的实际情况调整上述框架中的内容,并结合公司的官方公告、新闻报道和行业分析报告等资料来丰富细节。同时,请确保所描述的事实准确无误,避免主观评价和猜测。

鑫雁公司的发展小趣事

在短短几年内,聚洵半导体实现了业绩的快速增长。据公司董事长兼总经理张智才介绍,在2020年这一特殊年份,聚洵依然实现了业绩增长500%的佳绩,累计出货量超过2亿颗,成交客户数量达500多家。这一成绩不仅彰显了聚洵在产品研发和市场销售方面的实力,也赢得了业界的广泛认可。公司的典型终端客户包括美的、格兰仕、小米等知名品牌,进一步证明了聚洵产品在市场上的竞争力和影响力。

ept Connectors公司的发展小趣事

进入上世纪80年代,EPT Connectors公司迎来了一次重要的技术突破。公司成功推出了DIN 41612连接器,这一产品凭借其高性能和可靠性迅速在市场上获得了认可。随后,EPT又相继推出了PC/104和PC/104-Plus、Hm2.0、AdvancedTCA和MicroTCA等产品组,进一步巩固了其在连接器领域的领先地位。这些产品的推出不仅提升了EPT的品牌影响力,也为公司带来了可观的收益。

Einfochips公司的发展小趣事

为了满足不断增长的ASIC和嵌入式服务需求,Einfochips公司决定在印度孟买附近的Pune设立一家新的设计中心。该设计中心将为亚洲和北美客户提供服务,主要涉及存储网络、无线通信和消费多媒体等领域。这一举措不仅加强了Einfochips在亚洲和北美市场的地位,还为其未来的发展奠定了坚实基础。

Ametek公司的发展小趣事

随着硬件设计和验证领域的发展,SystemVerilog逐渐成为行业内的主流语言。Einfochips公司敏锐地捕捉到了这一趋势,并决定为客户提供从其他传统语言和环境转变到SystemVerilog的验证迁移服务。这一服务的推出,不仅帮助客户提高了设计和验证效率,还进一步扩大了Einfochips在硬件设计和验证领域的市场份额。

American Custom Components公司的发展小趣事

American Custom Components公司自创立之初,就致力于电子元器件的创新研发。在一次技术研讨会上,公司的研发团队发现了一种新型的半导体材料,具有出色的导电性和稳定性。经过数月的实验和验证,团队成功地将这种材料应用于新产品中,推出了一款性能卓越的集成电路。这一创新技术不仅提升了公司的产品质量,还赢得了市场的广泛认可,为公司的发展奠定了坚实基础。

问答坊 | AI 解惑

卖儿卖女跪求高手指点

我做的毕业设计是基于AT89S52单片机的信号发生器,要求实现通过键盘选择不同频率不同波形的输出。具体要求: 1、实现1HZ,2HZ、5HZ、10HZ、20HZ、50HZ、100HZ、200HZ、500HZ、1KHZ、2KHZ、5KHZ、10KHZ、20KHZ、50KHZ、100KHZ、200KHZ、500KHZ的四 ...…

查看全部问答>

一个雷人的例子,2段网线的接法!

昨天去了顺德,才600米,客户说网桥不好,ping出现丢包,我去一看才知道: 距离不到1公里左右,灵敏度到-60dbm,信号非常好,但是客户说丢包,ping楼顶也是不行。 我只有过去,一看差点晕倒,雷人 网桥后接tplink的交换机,45元那种; 从2楼到1 ...…

查看全部问答>

2410下的音频采集

最近在做2410下的音频采集,搞了个驱动程序和Makefile文件,老MAKE不成功,不知道啥问题,高手们能否指点一下,有相关资料能否共享一下啊…

查看全部问答>

XScale体系结构及编译优化问题.pdf

XScale体系结构及编译优化问题.pdf…

查看全部问答>

关于单片机晶振的问题

最近我用的一款单片机...ST的UPSD3254A,也就是款和51指令都兼容,只不过加了很多额外功能的单片机 现在用的是40M的晶振,本来...单片机X1,X2脚出来,接个晶振再接2个27P的瓷片电容对地就万事了,可有块板上,在晶振X1脚边上又引出根线接了2uH的 ...…

查看全部问答>

8255A芯片编程,C端口的各位则采用置0/置1的方式

用8255A的A端口接8位二进制输入,B端口和C端口各接8只发光二极管显示二进制数。编写一段程序,把A端口的读入数据送B端口显示,而C端口的各位则采用置0/置1的方式显示A端口的值。 不知道怎么写啊,先谢谢各位了,在8086环境下。…

查看全部问答>

在wince 5.0 C#无法打断点

在wince 5.0 下,我用的开发环境是VS2005,用MFC工程就能打断点,用.net的C#无法打断点,该如何解决?…

查看全部问答>

strmiids.lib、dmoguids.lib库找不到?

环境:EVC4+SP4 PB5.0 问题:使用Directshow,包含头文件,      这些头文件使用到了库文件strmiids.lib、dmoguids.lib,提示错误是canno open file srmiids.lib、dmoguids.lib,      我看了哈SDK,是 ...…

查看全部问答>

请教各位高手~~~~~~~

在ce下如何使目标平台识别如U盘和摄像头一类的usb设备啊?各个版本的ce在这个问题上处理的方法区别大么? 谢谢!…

查看全部问答>

求助:LED渐变,程序中的延时问题

以下这个控制LED渐明渐暗的程序,现在的现象是:第一个灯渐明渐暗后,紧接着第二个灯开始渐明渐暗了,而我想要的效果是:当第一个灯亮完熄灭后,停留数秒一段时间,才渐渐点亮第二个灯,我尝试在几个位置加上delay延时,但没效果,有时还会出现闪烁 ...…

查看全部问答>