历史上的今天
今天是:2025年01月31日(星期五)
2019年01月31日 | 基于stm32单片机的模拟IIC时序
2019-01-31 来源:eefocus
我下面要说的是基于stm32单片机的模拟IIC时序,以及是一些要注意的事项;结合自己所做的MMA7455加速度传感器,我把模拟IIC的源代码贴了出来,大家可以参考一下。
1.因为在IIC协议中,当总线空闲的时候,SDA和SCL都为高电平,所以硬件电路中SDA和SCL引脚都要接上拉电阻。
2.注意开始信号,停止信号,响应信号,非响应信号的时序,特别是要留意高低电平时间的延时:
3.应答信号分为主机应答和从机应答:
主机应答是在主机从从机中读取数据时每次读取完一个字节的数据后主机给从机的一个应答信号,表示主机已收到数据了。
从机应答是指主机给从机发送数据时从机给主机的应答,给一个应答就代表从机已经收到了数据,为主机接下来的工作做个判断。主机在核查从机的应答信号的时候,必须先将SDA总线拉高,释放总线。
非应答信号是主机给从机的,当读取完一字节数据以后,主机不再去读取数据就给从机一个非应答信号,接着一个停止信号,直接给停止信号也是可以结束此次读操作,但是会对后面的操作带来影响。

主机发送一个字节的数据,从高位开始发送。SCL位高电平的时候,数据必须保持稳定,所以可以在SCL为低电平时组织数据;同理读取数据也是类似的。

4.SCL是单向的,由master控制。SDA是双向的,master可以控制,slaver也可以控制。
主机给从机写入数据时,SDA在SCL低电平的时候变化,SDA在SCL高电平的时候保持不变,也就是说在SCL上升沿时写入数据。
主机从从机读取数据时,SDA在SCL高电平的时候变化,SDA在SCL低电平的时候保持不变,也就是说在SCL下升沿时读出数据。
需要注意的是:写入数据时就是要先改变SDA的值再去制造一个SCL的上升沿,读取数据时就是要先改变SDA的值再去制造一个SCL的下降沿,先后顺序必须把握好。
下面是我在用MMA7455加速度传感器时,写的IIC时序;程序已验证过可使用,大家可以参考一下
#define mma7455addree 0x3a //每个设备有还会有自己的设备地址
#define sdah GPIO_SetBits(GPIOA,GPIO_Pin_0)
#define sclh GPIO_SetBits(GPIOA,GPIO_Pin_1)
#define sdal GPIO_ResetBits(GPIOA,GPIO_Pin_0)
#define scll GPIO_ResetBits(GPIOA,GPIO_Pin_1)
void sdain()
{
GPIO_InitTypeDef i2csdain;
i2csdain.GPIO_Pin = GPIO_Pin_0;
i2csdain.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置为浮空输入
GPIO_Init(GPIOA, &i2csdain);
}
void sdaout()
{
GPIO_InitTypeDef i2csdaout;
i2csdaout.GPIO_Pin = GPIO_Pin_0;
i2csdaout.GPIO_Mode = GPIO_Mode_Out_PP;
i2csdaout.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &i2csdaout);
}
void sclout()
{
GPIO_InitTypeDef i2csclout;
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
i2csclout.GPIO_Pin = GPIO_Pin_1;
i2csclout.GPIO_Mode = GPIO_Mode_Out_PP;
i2csclout.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &i2csclout);
}
void i2cst() //启动信号的函数
{
sdaout();
sclh;
sdah;
Delay_us(10);
sdal;
Delay_us(5);
scll;
}
void i2ced() //停止信号的函数
{
sdaout();
sdal;
sclh;
Delay_us(10);
sdah;
Delay_us(5);
}
void i2cack() //等待信号,当发送器发送一个数据之后,需要等待从接收端发过来的应答信号,主机等待从机应答
{
unsigned char i;
sdain();
sclh;
Delay_us(5);
while((GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1) && (i < 250)) i++; //检测到SDA为低,也就是从机产生了应答信号或者从机没有应答信号,导致超时,都会跳出该函数。
scll;
Delay_us(10);
}
void i2cwrda(unsigned char da) //写入一个字节的函数
{
unsigned char i, temp, temp1;
sdaout();
scll;
temp1 = da;
for(i = 0; i < 8; i++)
{
scll;
temp = temp1;
temp = temp & 0x80;
if(temp == 0x80)
{
sdah;
}
else
{
sdal;
}
Delay_us(15);
sclh;
Delay_us(15);
temp1 = temp1 << 1;
}
}
unsigned char i2creda() //读取一个字节的函数
{
unsigned char i, j;
sdain();
for(i = 0; i < 8; i++)
{
sclh;
Delay_us(5);
j = (j << 1) | GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
scll;
Delay_us(5);
}
return j;
}
void mma7455Write(unsigned char addr, unsigned char dat) //往 mma7455的addr地址写入addr数据
{
i2cst();
i2cwrda(0x3a);
i2cack();
i2cwrda(addr);
i2cack();
i2cwrda(dat);
i2cack();
i2ced();
}
unsigned char mma7455Read(unsigned char addr) //读取mma7455的addr地址的数据
{
unsigned char num1;
i2cst();
i2cwrda(0x3a);
i2cack();
i2cwrda(addr);
i2cack();
i2cst();
i2cwrda(0x3b);
i2cack();
num1 = i2creda();
i2ced();
return (num1);
}
其实多机通信可以用一个简单的办法解决,只要在只要在上面mma7455Write,mma7455Read函数中用if语句根据不同的标志位选择相应的设备地址,要读取地址即可。大家可以去试一下!
上一篇:STM32 定时器周期动态修改
史海拾趣
|
我在写bootloader,在串口这遇到点麻烦,各位大牛给点意见 我将bootloader分为两个阶段,现在第一阶段差不多完成了,就差实现从串口烧录第二阶段到flash的功能 思路是这样的,在kermit下send boot_sencond.bin,我想问下,在发送完后,boot_sencon ...… 查看全部问答> |
|
我的问题是:我有个硬件可以读取一些数据,硬件上有个按钮,按这按钮后读取后的数据通过硬件上的SIM卡,将数据发送到数据接收端(比如用短信猫接收信息). 现在我需要完成开发涉及的操作是:读取后的数据通过硬件上的SIM卡,将数据发送到数据接收端. 请问 ...… 查看全部问答> |
|
请问:拉丁文(西班牙语)是否支持7bit编码方式? 在给客户做一个短信终端,客户要求已7bit编码方式实现拉丁文的短信内容 多谢!… 查看全部问答> |
|
我的ISE是10.1版本的,器件中Virtex4中只有XC4VLX15、XC4VLX25、XC4VSX25、XC4VFX12这几个器件,缺少很多其它的,不知道是怎么回事,请大家帮下忙,应该怎么弄才会出现其它的?我要用到Virtex4中XC4VFX20… 查看全部问答> |
|
首先祝大家新年快乐!希望在新的一年大家都能够一帆风顺,心想事成! 然后弱弱地发一本某公司的内部资料。。。之前发在了百度,后来才发现上面有公司名字,不知道这会不会引起不必要的纠纷啊? … 查看全部问答> |




