51开发板除了使用AT24C02,还有没有什么方法或者函数可以储存程序里的多个数据?

      以按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值。这样做的效果是:如果有按键按下了,调用该read()函数的进程,就成功读取到数据,应用程序得到继续执行;倘若没有按键按下,则要一直处于休眠状态,等待这有按键按下这样的事件发生。

这种功能在一些场合是适用的,但是并不能满足我们所有的需要,有时我们需要一个时间节点。倘若没有按键按下,那么超过多少时间之后,也要返回超时错误信息,进程能够继续得到执行,而不是没有按键按下,就永远休眠。这种例子其实还有很多,比方说两人相亲,男方等待女方给个确定相处的信,男方不可能因为女方不给信,就永远等待下去,双方需要一个时间节点。这个时间节点,就是说超过这个时间之后,不能再等了,程序还要继续运行,需要采取其他的行动来解决问题。

      单片机编程,等待IIC设备一个事件的发生,如果在允许的时间内发生了就返回1(SUCCESS),否则返回0(ERROR)。

,如果在限定的时间内(CPU将100000减到0),还没有成功写入,那么就将返回超时错误,页写函数也会返回写入失败的错误信息。之后,任务重新得到了运行。

      对于单片机这样通常单任务运行的状况,必须采取这样的措施。如果没有超时限制,那么程序将陷入死机,不能再继续运行。

     对于类似的场景,linux系统使用poll功能来解决这样的问题。而且,与上述单片机等待方式不同,linux系统再调用poll()函数时候,如果没有发生需要的事件,那么进程进入休眠。如果在限定的时间内得到需要的事件,那么成功返回,如果没有则返回超时错误信息。

     可见,等待期间将进程休眠,利用事件驱动来唤醒进程,将更能提高CPU的效率。下面,以一个应用例程来说明poll的应用程序使用方法:

       例程实现的功能是这样的:用poll()函数监测按键按下的事件,如果按下了就将键值打印出来;如果超过5S,还没有按键按下,就打印出超时信息。

fds         可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回


/* 根据实际情况,标记事件类型 */
/* 如果mask为0,那么证明没有请求事件发生;如果非零说明有时间发生 */

      上述代码展示了一个poll()函数功能,具体对应的底层驱动实现细节。利用这样的框架,我们可以写出类似驱动的poll功能。但是,这个框架很难理解,不知道为什么这样编写?为此,我们需要了解linux系统poll功能实现的机制。

     从应用程序调用poll()函数开始,一直到调用drivers_poll函数,期间的过程很复杂,捡主要的内容列出来:

      由此可见,我们的drivers_poll()函数,是系统在执行sys_poll()过程中的一个调用,调用的目的是“将进程挂接到等待队列下”和“返回事件类型mask”。当已经发生了请求事件,那么通过标记mask非0,if (do_pollfd(pfd, pt))判断为真,令count++,从而可以直接令poll()函数成功返回。如果还没有发生请求的事件,那么mask被标记为0,进程将通过函数poll_schedule_timeout()陷入休眠状态。一旦发生了请求的事件,因为之前已经将进程挂接到等待队列下,所以进程将被唤醒,重新执行drivers_poll(),而显然此时能够成功返回。

本系列教程将HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用

在之前的标准库中,STM32的硬件IIC非常复杂,更重要的是它并不稳定,所以都不推荐使用。


但是在我们的HAL库中,对硬件IIC做了全新的优化,使得之前软件IIC几百行代码,在HAL库中,只需要寥寥几行就可以完成 那么这篇文章将带你去感受下它的优异之处

这可能是目前关于STM32CubeMX的硬件iic 讲的最全面和详细的一篇文章之一了

通过本篇博客您将学到:

IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。

PS: 这里要注意IIC是为了与低速设备通信而发明的,所以IIC的传输速率比不上SPI

IIC一共有只有两个总线: 一条是双向的数据线SDA,一条是串行时钟线SCL

所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每一个设备都对应一个唯一的地址。

关于IIC的讲解,已经单独整理了一篇文章:

《IIC原理超详细讲解—值得一看》。
如果对IIC还不是太了解的朋友请移步到这篇文章中

IIC起始信号和终止信号:

  • 起始信号:SCL保持高电平,SDA由高电平变为低电平后,延时(>4.7us),SCL变为低电平。

  • 停止信号:SCL保持高电平。SDA由低电平变为高电平。

IIC信号在数据传输过程中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

SCL=1时 数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号。

也就是在IIC传输数据的过程中,SCL时钟线会频繁的转换电平,以保证数据的传输

每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,

应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答

应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

每发送一个字节(8个bit)在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须回一个ACK应答信号给发送器,这样才能进行数据传输。

应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答,

这里我们仅介绍基于AT24C02的IIC通信

24C02是一个2K Bit的串行EEPROM存储器(掉电不丢失),内部含有256个字节。在24C02里面有一个8字节的页写缓冲器。
WP:写保护引脚,接高电平只读,接地允许读和写

以看出对于不同大小的24Cxx,具有不同的从器件地址。由于24C02为2k容量,也就是说只需要参考图中第一行的内容:
AT24C设备地址为如下,前四位固定为1010,A2~A0为由管脚电平。AT24CXX EEPROM Board模块中默认为接地。所以A2~A0默认为000,最后一位表示读写操作。所以AT24Cxx的读地址为0xA1,写地址为0xA0。

写24C02的时候,从器件地址为(0xA0);
读24C02的时候,从器件地址为(0xA1)。

芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。

对应的修改 A2A1A0 三位数据即可

  1. MCU先发送一个开始信号(START)启动总线

  2. 等待应答信号(ACK)

  3. 发送数据的存储地址。24C02一共有256个字节的存储空间,地址从0x00~0xFF,想把数据存储>在哪个位置,此刻写的就是哪个地址。

  4. 发送要存储的数据第一字节、第二字节、…注意在写数据的过程中,E2PROM每个字节都会>回应一个“应答位0”,老告诉我们写E2PROM数据成功,如果没有回应答位,说明写入不成功。

  5. 发送结束信号(STOP)停止总线

在写数据的过程中,每成功写入一个字节,E2PROM存储空间的地址就会自动加1,当加到0xFF后,再写一个字节,地址就会溢出又变成0x00。

写数据的时候需要注意,E2PROM是先写到缓冲区,然后再“搬运到”到掉电非易失区。所以这个过程需要一定的时间,AT24C02这个过程是不超过5ms!
所以,当我们在写多个字节时,写入一个字节之后,再写入下一个字节之前,必须延时5ms才可以

  1. MCU先发送一个开始信号(START)启动总线

  2. 注意:这里写操作是为了要把所要读的数据的存储地址先写进去,告诉E2PROM要读取哪个地址的数据。

  3. 发送要读取内存的地址(WORD ADDRESS),通知E2PROM读取要哪个地址的信息。

  4. 重新发送开始信号(START)

  5. E2PROM会自动向主机发送数据,主机读取从器件发回的数据,在读一个字节后,MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据

  6. 如果不想读了,告诉E2PROM不想要数据了,就发送一个“非应答位NAK(1)”。发送结束信号(STOP)停止总线

E2PROM支持连续写操作,操作和单个字节类似,先发送设备写操作地址(DEVICE ADDRESS),然后发送内存起始地址(WORD ADDRESS),MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据。E2PROM的地址指针会自动递增,数据会依次保存在内存中。不应答发送结束信号后终止传输。

设置高速外部时钟HSE 选择外部时钟源

点击I2C1 设置为I2C 因为我们的硬件IIC 芯片一般都是主设备,也就是一般情况设置主模式即可

  • I2C Speed Mode: IIC模式设置 快速模式和标准模式。实际上也就是速率的选择。

因为我们需要将AT24C02中存储的数据发送到上位机上,所以需要设置下串口

这里设置为异步通信,其他的默认即可

串口如有不懂,请看这篇文章

我的是 外部晶振为8MHz

  • 2PLL锁相环倍频9倍

  • 3系统时钟来源选择为PLL

  • 5 使能CSS监视时钟

32的时钟树框图 如果不懂的话请看《【STM32】系统时钟RCC详解(超详细,超全面)》

新建的工程所有配置都是默认的 我们需要自行选择下载模式,勾选上下载后复位运行

在i2c.c文件中可以看到IIC初始化函数。在stm32f1xx_hal_i2c.h头文件中可以看到I2C的操作函数。分别对应轮询,中断和DMA三种控制方式

上面的函数看起来多,但是只是发送和接收的方式改变了,函数的参数和本质功能并没有改变
比方说IIC发送函数 还是发送函数,只不过有普通发送,DMA传输,中断 的几种发送模式

这里我们仅介绍下普通发送,其他的只是改下函数名即可

    • Timeout 最大传输时间,超过传输时间将自动退出传输函数

功能:IIC读一个字节

  • Timeout: 最大读取时间,超过时间将自动退出读取函数

/* 第1个参数为I2C操作句柄

   第5个参数为发送的数据的起始地址

功能: IIC写多个数据 该函数适用于IIC外设里面还有子地址寄存器的设备,比方说E2PROM,除了设备地址,每个存储字节都有其对应的地址

  • MemAddress: 从机寄存器地址 ,每写入一个字节数据,地址就会自动+1

    • 写入数据的字节类型 8位还是16位

  • *pData: 需要写入的的数据的起始地址

  • Size: 传输数据的大小 多少个字节

  • Timeout: 最大读取时间,超过时间将自动退出函数

在传输过程,寄存器地址和源数据地址是会自加的。

至于读函数也是如此,因此用HAL_I2C_Mem_Write和HAL_I2C_Mem_Read,来写读指定设备的指定寄存器数据是十分方便的,让设计过程省了好多步骤。

如果只往某个外设中写数据,则用Master_Transmit。 如果是外设里面还有子地址,例如我们的E2PROM,有设备地址,还有每个数据的寄存器存储地址。则用Mem_Write。

在mian.c文件前面声明,AT24C02 写地址和读地址 ,定义写数据数组,和读数据数组

AT24C02的IIC每次写之后要延时一段时间才能继续写 每次写之后要delay 5ms左右 不管硬件IIC采用何种形式(DMA,IT),都要确保两次写入的间隔大于5ms;

读写函数最后一个超时调整为1000以上 因为我们一次写8个字节,延时要久一点

AT24C02页写入只支持8个byte,所以需要分32次写入。这不是HAL库的bug,而是AT24C02的限制,其他的EEPROM可以支持更多byte的写入。

当然,你也可以每次写一个字节,分成256次写入,也是可以的 那就用注释了的代码即可

我要回帖

更多关于 开发板使用 的文章

 

随机推荐