技术标签: c byte 存储 工作 interface 日历
I2C总线是由Philips公司开发的2线式串行总线,由于其简单、高效、互联成本小而被广泛地用于微控制器与外围设备的连接。AT91SAM7X256是Atmel公司于2005年推出的基于ARM7的工业级芯片,他以体积小、功耗低、连接方式广泛、处理资源丰富、控制灵活等特点受到嵌入式领域开发人员的重视。本文介绍AT91SAM7X256的I2C控制器TWI接口(two-wired interface)的使用方法,并以I2C设备E2PROM和日历时钟芯片为例,实现AT91SAM7X256对时间数据的读取与存储。同时,为了验证时间数据的读取与存储是否正确,使用AT91SAM7X256的在线仿真器J-LINK将E2PROM中的数据读至内存进行检查。
硬件设计
2.1 硬件模块结构
电路的硬件模块结构如图1所示。
AT91SAM7X256的TWI接口由一根时钟线TWCK和一根数据线TWD组成,产生的信号时序符合I2C总线规范;PCF8563是Philips公司推出的一款内含I2C总线接口功能的工业级时钟芯片;AT24C08是Atmel公司推出的符合I2C规约的两线串口E2PROM。AT91SAM7X256的TWCK和TWD分别与芯片PCF8563和AT24C08的SCL与SDA相连,CPU通过TWI接口将时间数据读出并存储。为了保证CPU不冲突的访问PCF8563和AT24C08,本文将AT24C08的A2管脚接高电平。由于I2C总线空闲时为高电平,所以为实现“线与”功能,总线上连接的设备均是集电极开路的,因此总线需外接上拉电阻R。AT91SAM7X256的TWI有主从2种工作模式,本文中AT91SAM7X256作为控制方,应工作于主模式。
2.2 AT91SAM7X256的TWI接口
AT91SAM7X256的TWD和TWCK管脚与设备的I/O管脚复用,同时AT91SAM7X256采用单独控制功能单元的省电方案,电源管理单元PMC控制各功能单元的时钟是否工作,所以要使用TWI接口,需要首先配置TWD和TWCK为外设连线和开路状态,其次配置PMC使TWI时钟处于工作状态。
TWI接口可提供高达400 kb/s的传输速率,为使得数据的传输速率面向不同应用,可以通过配置时钟脉冲发生器的控制寄存器TWI_CWEG调整TWCK的信号频率。
TWI接口产生的信号时序符合I2C总线规范,当读/写1个字节数据时,主设备需提供从设备的设备地址、内部地址、读写控制以及起始标志和停止标志。在数据的收发过程中,主要使用控制寄存器TWI_CR、主模式寄存器TWI_MMR、内部地址寄存器TWI_IADR、状态寄存器TWI_SR、传输保持寄存器TWI_THR和接收保持寄存器TWI_RHR。从设备地址在TWI_MMR中设置,从设备的内部地址在TWI_IADR中设置;在TWI_CR中设置是否发送起始信号和停止信号;NAK(无应答)、OVER(运行错误)、TXRDY(发送准备好)、RXRDY(接收准备好)、TX-COMP(传输完成)等状态位通过查询WI_SR得到。
写数据的过程包括:当TWI_THR写入数据后,CPU产生起始信号启动传输,TWI_THR中的数据经过并串转换后由TWD传输出去,当CPU收到从设备的应答信号后,TWI_SR的TXRDY将自动置“1”,说明数据已写入从设备。读数据的过程包括:CPU发出起始信号后,若TWI_SR的RXRDY位为“1”,则说明TWI_RHR中有数据等待接收,当TWI_RHR中的数据被读出后,则RXRDY自动置为“0”。当读/写数据完毕后,CPU将产生一个停止信号结束传输,TWI_SR的TXCOMP将自动置“1”。
2.3 PCF8563日历时钟芯片的使用方法
按I2C协议规约,PCF8563具有惟一的设备地址0A2H。本文重点研究PCF8563时、分、秒数据的读取方法,在此用到的内部寄存器包括控制/状态寄存器1(地址为00H)、秒寄存器(地址为02H)、分寄存器(地址为03H)、小时寄存器(地址为04H)。由于寄存器中以BCD格式存储时、分、秒数据,所以各时间时间寄存器的高位无效。
为使PCF8563工作于普通模式,需要将控制/状态寄存器1置为00H,同时为了存储正确的时间数据,需要将读到的数据中无效的高位进行屏蔽。若需要校对时间,只需对时、分、秒寄存器进行写操作即可。
2.4 AT24C08的使用方法
AT24C08是容量为8192 b(1024 B)的E2PROM。AT24C08内部分为4页,每一页有256字节单元,所以若要访问某个单元则需要10位进行寻址,其中最高两位是页地址,低8位是页内地址。设备地址的定义如图2所示,其中P1P0对应页地址,管脚A2可为AT24C08设定两组设备地址。当A2为低电平时,4页的设备地址分别为0A0H,0A2H,0A4H,0A6H;当A2为高电平时;反之为0A8H,0AAH,0ACH,0AEH。因此,为了避免AT24C08与PCF8563的设备地址冲突,需将A2连接高电平。
AT24C08的写操作支持“字节写”和“页面写”两种方式。“字节写”方式中每写一个字节均需主设备提供起始信号、设备地址、内部地址以及停止信号;“页面写”方式即连续写数据,需主设备提供起始标志、设备地址以及内部地址,数据全部写完后再发送停止标志。
AT24C08的读操作支持“当前地址读”、“随机读”和“顺序读”3种方式。“当前地址读”表示从当前内部地址单元读出1个字节,所以主设备仅需提供起始信号、设备地址和停止信号;“随机读”表示从任意内部地址单元读出1个字节,所以主设备需要先提供1次起始信号、设备地址、写操作、设备内部地址和停止信号,设定设备的内部地址,之后再按“当前地址读”方式读数据即可;“顺序读”表示从当前地址开始连续读多个字节,所以主设备需提供起始信号、设备地址、读操作,数据全部读完后再发送停止信号。
为了快速读写数据,本文采用页面写的方式将数据写入AT24C08;采用“随机读”和“顺序读”相结合的方式读取AT24C08数据。
软件设计
3.1 TWI初始化程序的设计
根据TWI的功能特点,TWI初始化的初始化包括以下4步:
(1)配置PIO控制器使复用管脚驱动TWI信号;
(2)配置PMC使TWI时钟处于工作状态;
(3)配置TWI为主工作模式。本文CPU为主设备,日历和存储芯片为从设备;
(4)设置数据传输速率,配置TWI时钟波形发生器寄存器。
IIC驱动程序:
#include <AT91SAM7X256.H>
#include <Lib_AT91SAM7X256.H>
#include "main.h"
#include "twi_driver.h"
#define ERROR (AT91C_TWI_NACK)//状态寄存器的一位,如果置1表示从机未应答
//*============================================================================
//* TWI初始化
//*============================================================================
//*----------------------------------------------------------------------------
//* 函数名: AT91F_SetTwiClock
//* 功能: 设置TWI时钟波形发生寄存器
//*----------------------------------------------------------------------------
void AT91F_SetTwiClock(void)
{
int sclock;
/* Here, CKDIV = 1 and CHDIV=CLDIV ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6)*/
sclock = (10*AT91B_MCK /AT91C_TWI_CLOCK);
if (sclock % 10 >= 5)
sclock = (sclock /10) - 5;
else
sclock = (sclock /10)- 6;
sclock = (sclock + (4 - sclock %4)) >> 2; // div 4
AT91C_BASE_TWI->TWI_CWGR = ( 1<<16 ) | (sclock << 8) | sclock ;
}
//*----------------------------------------------------------------------------
//*函数名: AT91F_TWI_Open
//* 功能: 初始化TWI接口
//*----------------------------------------------------------------------------
void AT91F_TWI_Open(void)
{
// 配置I/O口作为TWI功能
AT91F_TWI_CfgPIO ();
// 将TWD和TWCK定义为开路
AT91F_PIO_CfgOpendrain(AT91C_BASE_PIOA,Twd_Twck);
// 开TWI时钟源
AT91F_TWI_CfgPMC ();
// 配置TWI工作在主模式,禁止TWI中断,采用查询读写方式
AT91F_TWI_Configure (AT91C_BASE_TWI);
// 设置TWI时钟波形发生寄存器
AT91F_SetTwiClock();
}
//*=============================================================================
//* 写EPROM
//*=============================================================================
//*----------------------------------------------------------------------------
//* 函数名:AT91F_TWI_WriteByte
//* 功能: 发送(以字节为单位)数据到设备(EPROM)
//*参数说明:
// pTwi:指向TWI结构的首地址-----设为AT91C_BASE_TWI
// mode:TWI的工作模式----用于设置WTI的主模式寄存器(内部器件地址长度,主机读写方向,器件地址),见DATASHEET
// int_address:器件的内部地址(要写入的位置)
// data2send:代写的数据缓冲区地址
// nb:写入的字节数
//*----------------------------------------------------------------------------
int AT91F_TWI_WriteByte(const AT91PS_TWI pTwi ,int mode, int int_address, char *data2send, int nb)
{
unsigned int status,counter=0,error=0;
// 设置TWI内部地址寄存器---可设为0,1,2,3位内部地址
if ((mode & AT91C_TWI_IADRSZ) != 0) pTwi->TWI_IADR = int_address;
// 设置TWI的主模式寄存器(此处为主机写)
pTwi->TWI_MMR = mode & ~AT91C_TWI_MREAD;
// 如果只写1个字节
if(nb <2)
{
//发送START状态,使能传输,传输完后发送STOP状态
pTwi->TWI_CR = AT91C_TWI_START | AT91C_TWI_MSEN | AT91C_TWI_STOP;
//将数据写入发送保持寄存器
pTwi->TWI_THR = *data2send;
}
else //如果一次写入多个字节
{
for(counter=0;counter<nb;counter++)
{
//发送START状态,使能传输
pTwi->TWI_CR = AT91C_TWI_START | AT91C_TWI_MSEN;
//最后一个数据发送完后,接着发送STOP状态
if (counter == (nb - 1)) pTwi->TWI_CR = AT91C_TWI_STOP;
//读取状态寄存器
status = pTwi->TWI_SR;
//如果发送完一个字节后没收到应答信号,则表明有一次传输错误
if ((status & ERROR) == ERROR) error++;
while (!(status & AT91C_TWI_TXRDY))
{
status = pTwi->TWI_SR;
if ((status & ERROR) == ERROR) error++;
} //END while
//传输下一个字节
pTwi->TWI_THR = *(data2send+counter);
}//END for
}//END else
//读取状态寄存器
status = pTwi->TWI_SR;
//如果发送完一个字节后没收到应答信号,则表明有一次传输错误
if ((status & ERROR) == ERROR) error++;
//等待数据发送完
while (!(status & AT91C_TWI_TXCOMP))
{
//读取状态寄存器
status = pTwi->TWI_SR;
//如果发送完一个字节后没收到应答信号,则表明有一次传输错误
if ((status & ERROR) == ERROR) error++;
}//END while
return error;
}
//*=============================================================================
//* 读(EPROM)
//*=============================================================================
//*----------------------------------------------------------------------------
//* 函数名:AT91F_TWI_ReadByte
//* 功能: 从设备(EPROM)中读取一个字节的数据
//*参数说明:
// pTwi:指向TWI结构的首地址-----设为AT91C_BASE_TWI
// mode:TWI的工作模式----用于设置WTI的主模式寄存器(内部器件地址长度,主机读写方向,器件地址),见DATASHEET
// int_address:器件的内部地址(要写入的位置)
// data2read:数据缓冲区地址
// nb:要读出的字节数
//* Creation : 张正锋 2005-12-30 [email protected]
//*----------------------------------------------------------------------------
int AT91F_TWI_ReadByte(const AT91PS_TWI pTwi ,int mode, int int_address, char *data2read, int nb)
{
unsigned int status,counter=0,error=0;
// 设置TWI内部地址寄存器---可设为0,1,2,3位内部地址
if ((mode & AT91C_TWI_IADRSZ) != 0) pTwi->TWI_IADR = int_address;
// 设置TWI的主模式寄存器(此处为主机读)
pTwi->TWI_MMR = mode | AT91C_TWI_MREAD;
//如果只读取一个字节
if (nb == 1)
{
//发送START状态,接收完后发送STOP状态
pTwi->TWI_CR = AT91C_TWI_START | AT91C_TWI_STOP;
//读取状态寄存器
status = pTwi->TWI_SR;
//如果没收到应答信号,则表明有一次传输错误
if ((status & ERROR) == ERROR) error++;
//等待操作完
while (!(status & AT91C_TWI_TXCOMP))
{
status = pTwi->TWI_SR;
if ((status & ERROR) == ERROR) error++;
}
//将数据放入缓冲区
*(data2read) = pTwi->TWI_RHR;
} //END if
else //如果读取多个字节
{
//发送START状态,接收完后发送STOP状态
pTwi->TWI_CR = AT91C_TWI_START | AT91C_TWI_MSEN;
//读取状态寄存器
status = pTwi->TWI_SR;
//如果没收到应答信号,则表明有一次传输错误
if ((status & ERROR) == ERROR) error++;
//等待操作完
while (!(status & AT91C_TWI_TXCOMP))
{
status = pTwi->TWI_SR;
if ((status & ERROR )== ERROR) error++;
//查看是否收到一个字节数据
if(status & AT91C_TWI_RXRDY)
{
//将数据放入缓从区
*(data2read+counter++) = pTwi->TWI_RHR;
//如果是最后一次读取则发送STOP 状态
if (counter == (nb - 1)) pTwi->TWI_CR = AT91C_TWI_STOP;
} //END if
} //END while
} //END else
return error;
}
3.2 PCF8563驱动程序的设计
为了控制PCF8563的工作方式,需要对其写入控制字;为了得到PCF8563输出的时间信息,需要对其进行读操作。
#define PCF8563_ADDRESS (0xA2<<15) //PCF8563的地址为 1010 0010这里需要特别注意的是(0xA2<<15)而不是(0xA2<<16),主要是因为地址是7位的,不是8位地址,这个和51中不同,但最终写到iic总线上的数据是一样的。看一下TWI_MMR寄存器,第23位是不可用的,第12位是读写位,就明白了。
3.3 AT24C08驱动程序的设计
由于AT24C08由4个具有不同设备地址的页组成,且采用连续读写数据的操作方式,所以AT24C08的读写与PCF8563读写有以下几点区别。
(1)先设置TWI_CR的起始标志,之后通过TWI_RHR和TWI_THR读/写TWI接口的数据;发送最后一个数据之前,再设置TWI_CR的停止标志。
(2)对于多字节数据的读写,全部数据若没有传输完毕,便不发送停止信号,所以需通过判断TWI_SR寄存器中的TXRDY和RXRDY决定是否读TWI_RHR和写TWI_THR,而将是否出现停止信号作为是否停止发送和接收的判断依据。
(3)由于数据量和起始单元均是随机的,所以有可能出现一页写不下的情况,因此针对给定的数据量和起始单元参数需要计算出共需几页,以便在进行页面切换时更换设备地址。
测试程序:
#define AT91C_EEPROM_I2C_ADDRESS (0x50<<16) //EEPROM的地址为 0101 0000
void write_read_at24c02_test()
{
unsigned int i=0;
char buf0[20],buf[20],write[20]={0xa0,0xbb,0xcc,0xdd,0x55,0x44,0x33,0x22,0x11,0x01};
for(i=0;i<20;i++)
{
//x[i]=0;
buf0[i]=0;
write[i]=i;
}
i =AT91F_TWI_WriteByte(AT91C_BASE_TWI, AT91C_EEPROM_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0x00, write, 10);
i =AT91F_TWI_WriteByte(AT91C_BASE_TWI, AT91C_EEPROM_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0x10, &write[10], 10);
for(i=0;i<0xfff;i++);
i=AT91F_TWI_ReadByte(AT91C_BASE_TWI, AT91C_EEPROM_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0x00, buf, 10);
i=AT91F_TWI_ReadByte(AT91C_BASE_TWI, AT91C_EEPROM_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0x10, &buf[10], 10);
i=buf[0];
}
程序主函数:
#define AT91C_EEPROM_I2C_ADDRESS (0x50<<16) //EEPROM的地址为 0101 0000
#define PCF8563_ADDRESS (0xA2<<15) //PCF8563的地址为 1010 0010
int main()
{
unsigned int i=0;
char buf0[20],buf[20],write[20]={0xa0,0xbb,0xcc,0xdd,0x55,0x44,0x33,0x22,0x11,0x01};
for(i=0;i<20;i++)
{
//x[i]=0;
buf0[i]=0;
write[i]=i;
}
AT91F_LowLevelInit();
Init_GPIO();
Init_SPI0();
Init_UART0();
AT91F_TWI_Open();
for(i=0;i<0xfff;i++);
i =AT91F_TWI_WriteByte(AT91C_BASE_TWI, AT91C_EEPROM_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0x00, write, 10);
i =AT91F_TWI_WriteByte(AT91C_BASE_TWI, AT91C_EEPROM_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0x10, &write[10], 10);
for(i=0;i<0xfff;i++);
i=AT91F_TWI_ReadByte(AT91C_BASE_TWI, AT91C_EEPROM_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0x00, buf, 10);
i=AT91F_TWI_ReadByte(AT91C_BASE_TWI, AT91C_EEPROM_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0x10, &buf[10], 10);
i=buf[0];
i =AT91F_TWI_WriteByte(AT91C_BASE_TWI, PCF8563_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0X01, &buf0[0], 1);
for(i=0;i<0xfff;i++);
i=AT91F_TWI_ReadByte(AT91C_BASE_TWI, PCF8563_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE, 0x01, buf0, 10);;
i=buf0[0];
};
3.4 软件的调试与运行
本文采用keil FOR ARM 3.50开发环境和J-LINK仿真器进行软件的在线调试和加载运行。调用函数完成以下程序设计:首先从PCF8563连续读出若干数据并写入AT24C08;其次,将AT24C08中的数据读至数组变量中。在程序中的读完AT24C08数据后设置断点,观测数组中存放的数据,从而验证驱动程序的正确性。
特别提示下:PCF8563与AT24C16不能一起使用,因为设备地址冲突。
JavaScript是一种脚本语言,它由LiveScript改名而来,可能是为了更好地推广这个脚本语言(利用Java语言的知名度),因此Netscape公司在最后一刻决定将它改名为JavaScript,但其实与Java没有什么关系。JavaScript是一种基于客户端浏览器的(现在也有服务器端,如NodeJS),基于对象、事件驱动式的脚本语言。JavaScript也具有跨平台的特点。如同所
软件的开发工具在软件的开发过程中占很重要的地位,可以说是直接影响你软件开发的效率。比如:开发工具的常用功能、配置、以及优化等级。ARM处理器是英国Acorn有限公司设计的低功耗成本的第一款RISC微处理器。全称为Advanced RISC Machine。ARM处理器本身是32位设计,但也配备16位指令集,一般来讲比等价32位代码节省达35%,却能保留32位系统的所有优势。对于STM32的...
之前在模版之家找了个很简洁的大背景模版然后想做下图中间放个图片的效果,然后不管但是不管在css里设置backgro还是在style里设置,图片都uxianshi,然后F12检查,发现这一块有div,但是没有图片显示,当时就觉得是不是模版里设置了什么,覆盖了。解决方法就是把图片单独抽出来,放在img标签里。然后会有显示。再改改style。除此之外,当我想把type="file"的按钮换成这...
img_src = "https://so.gushiwen.org/RandCode.ashx" # 图片的地址# 存图片from urllib import requestrequest.urlretrieve(img_src, 'code.png')from PIL import Imageimage = Image.open('code.png')image.show()...
新建空白场景定向光源模拟太阳,场景偏红晕解决方法要解决的问题解决方法要解决的问题初学者在新建空白场景时,通常都知道用定向光源+大气雾模拟基本的天空光照效果.当通常会有个疑问,为什么整个世界偏红色,视觉效果糟糕,如下图:解决方法结果如下:...
IGBT全称绝缘栅双极晶体管,它是由BJT(双极型晶体管)和MOSFET(绝缘栅型场效应管)组成的复合全控型电压驱动式电子电力器件,即具有MOSFET的输入阻抗高、控制功率小、驱动电路简单、开关速度高的优点,又具有双极型功率晶体管的电流密度大、饱和压降低、电流处理能力强的优点。IGBT的结构IGBT在结构上类似于MOSFET,其不同点在于IGBT是在N沟道功率管MOSFET的N+基板(漏极)上加了...
文件的读写能大大提高linux程序编写的效率,减少代码量的同时,可以简化程序逻辑,在设计API交互时应用普遍。下面,我们将通过笔者编写的一个程序来对这个过程做一个初步的讲解,同时也希望通过对改程序的学习,能加强大家对文件的一些基本操作的认识。linux系统下,文件是基本组成单元,而文件指针则是最常见的操作单位,常常通过文件描述符(FD:file description)来对文件进行操作。文件操
可分为两个步骤: 第一步:每30分钟执行某个shell脚本 */30 * * * * sh /home/cromshell/twoMinutes.sh 第二步:编写特定的shell脚本 #!/bash/sh curl -X GET --header 'Accept: application/json'...
import android.content.Context;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.DisplayMetrics;import android.view.WindowManager;import android.widget.
我是16年上半年从软件开发转到算法工程师的,这些年AI,我亲眼见证了从“黑科技”跌入“俗学”的过程。早些年,在模式识别领域,例如人脸识别、语音识别等,大家都发力在数学算法(基于机器学习)的时候,虽然努力多年,但是因为技术缺陷精度却一直上不去,几乎没有实用价值。深度学习的引入在一次NIST竞赛中,有一个厂家突然爆发,一骑绝尘,直接把竞争对手甩下几个身位,也直接把很多识别技术(例如人脸识别)推到了实用...
ps:最近写代码被量子力学编译器恶心到了 自家的编译器不同版本对.和->的要求还不一样 本地编译器结果3平台编译结果1 什么鬼设定(没错gcc我说的就是你个憨批)这还写个p的代码了?我直接整个随机数宁自己量子力学去吧随机数的生成首先,要说明的是通常情况下我们看到的函数生成的随机数不是真的随机数而是通过一定的“魔术算法”生成的伪随机数 那么为啥嘞?rand与srand函数功能:int rand();//返回值为随机数,参数为空int srand(unsigned int seed);//对伪
基于docker的canal-server部署作业1. 目标启动一个canal-server实例监听目标mysql的操作2. 准备工作Ubuntu下安装Docker,并配置国内镜像部署一个mysql实例mysql实例也可以使用docker进行启动(参考菜鸟教程:Docker 安...