STM8L051F3 硬件I2C从机实例--新手导航

这是我写的第一篇有关于技术的文章,可能写的不是很好,仅供参考。

先介绍一下背景,我是第一次接触STM的芯片,以前接触过都是基于51的芯片,算是有一点基础吧。因为公司的项目需要用到STM8L051F3的硬件I2C作为从机送数据,把自己从接触到调通遇到的问题记录一下,有需要的可以参考一下。

这篇文章是以相对新手的水平去写给新手参考的,所以我会尽量写的详细点。

1.首先我们需要查看数据手册,看下硬件I2C的接口是哪个端口,我用的是STM8L051F3这一块芯片。STM8L051F3 硬件I2C从机实例--新手导航_第1张图片

这款芯片的I2C应该是不能复用别的端口的,反正手册我没查到,只能用C0跟C1。

2.接下来开始初始化I2C需要的设置。(这里我只讲7位地址的。)

void I2C_Init(void)

{

  CLK_PeripheralClockConfig(CLK_Peripheral_I2C1, ENABLE);	//初始化I2C时钟
  CLK->SWR=CLK_ICKCR_HSION;//时钟选择为HSI
  CLK->CKDIVR = 0;   //时钟不分频
  CLK->PCKENR1 = 0x08;						// 使能I2C时钟
  

	/* Init GPIO for I2C use */		//初始化端口C0和C1
	GPIOC->CR1 |= 0x03;
	GPIOC->DDR &= ~0x03;
	GPIOC->CR2 &= ~0x03;

	//初始化I2C寄存器
	I2C1->CR1 |= 0x01;				        	// Enable I2C peripheral
	I2C1->CR2 = 0x04;					      		// Enable I2C acknowledgement
	I2C1->FREQR = 16; 					      	// Set I2C Freq value (16MHz)
	//下面这里要重点说明一下,STM8L051F3的硬件I2C作为从机是可以具备2个地址的。(用不到的话等下不要使能地址2即可)
	I2C1->OARL = (0x44<< 1) ;	// 地址1 = 0x44.第0位是10位地址的0位,7位地址要左移1位。
	I2C1->OARH = 0x40;					      	// 此位需要置1,看手册。
	I2C1->OAR2 = (0x46 << 1)|0x01;	//这里是地址2 = 0x46的寄存器,第0位置1是使能2个地址,如果用不到直接屏蔽此语句即可。
	
	I2C1->ITR	= 0x07;					      // all I2C interrupt enable  使能I2C中断

}

3.记得开启中断(enableInterrupts();),下面看中断内容。

中断里面我们需要2组数组来储存收发的数据,这个自己在主函数里定义2个全局变量数组,大小为你自己想需要收发的数据看有多少了。

#define MAX_Id 10
u8 Slave_Buffer_Tx[MAX_Id];
u8 Slave_Buffer_Rx[MAX_Id];

下面是一个广播地址的

INTERRUPT_HANDLER(I2C_IRQHandler,29)
{
unsigned char Add;
unsigned char Nuse;
static unsigned char RX_Cnt=0;

if(I2C1->SR2&0X0F) //I2C 出现错误
{
I2C1->SR2&=0xf0;
//I2C->CR2|=1<<7;
//I2C->CR2&=~(1<<7);
RX_Cnt=0;
I2C_Tx_Idx=0;
I2C1->SR2 |=0x02;
}

if(I2C1->SR1&0X02) //地址匹配
{
(void)(I2C1->SR3); //先读I2C_SR1,再读I2C_SR3,就可以清除ADDR
// I2C->DR = 0X00;
I2C_Tx_Idx=0;
I2C_Rx_Idx=0;
I2C1->DR = Slave_Buffer_Tx[I2C_Tx_Idx++];
}
if(I2C1->SR1&0X10) //停止条件
{
Nuse = I2C1->CR2; //清除停止位(SR1第4位),先读SR1,再写CR2
nop();
I2C1->CR2 = Nuse;
}
if(I2C1->SR1&0X04) //BTF位,先读SR1,再读或者写DR寄存器清除
{
Nuse=I2C1->DR;
// I2C->DR=Nuse;
}
if(I2C1->SR1 & 0x40) //RXNE,数据寄存器是否为空,0空1非
{
//将收到的数据储存到RX数组中。虽然我们是从机,用不到这里的数据,但是这里必须读DR寄存器收取数据.不然会出错
Slave_Buffer_Rx[I2C_Rx_Idx++]=I2C1->DR;
if(I2C_Rx_Idx>=MAX_Id)
{
I2C_Rx_Idx=0;
}
}
if(I2C1->SR1 & 0x80) //将发送的数据放入DR寄存器,清除TXE
{
I2C1->DR = Slave_Buffer_Tx[I2C_Tx_Idx++];
if(I2C_Tx_Idx>=MAX_Id)I2C_Tx_Idx=0;
}

}

下面是2个广播地址的

//这款芯片的中断文件在这里文件里面(stm8l15x_it.c),I2C的中断向量就是这个。
INTERRUPT_HANDLER(I2C_IRQHandler,29)
{
unsigned char Add;
unsigned char Nuse;
static unsigned char RX_Cnt=0;
B_I2C *base;

if(I2C1->SR2&0X0F) //I2C 出现错误中断的处理。没特别的中断需要处理的话,这里直接复制就好。
{
I2C1->SR2&=0xf0;
RX_Cnt=0;
I2C_Tx_Idx=0;
I2C1->SR2 |=0x02;
}

//第七位判断是从机地址1还是地址2,选择等下接收到的数据要放在哪个结构体里面(或者你们自己替换成变量也可以的,只是把收到的数据存起来而已,之后我们才可以去调用)
if(I2C1->SR3&0x80)
{
base = &B_I2C_p1;//add1
}
else
{
base = &B_I2C_p2_0x46;//add2
}

if(I2C1->SR1&0X02) //SR1的第一位会检测广播地址是否匹配
{
(void)(I2C1->SR3); //先读I2C_SR1,再读I2C_SR3,就可以清除ADDR
// I2C->DR = 0X00;
I2C_Tx_Idx=0;
I2C_Rx_Idx=0;
I2C1->DR = Slave_Buffer_Tx[I2C_Tx_Idx++];
}
if(I2C1->SR1&0X10) //停止条件
{
Nuse = I2C1->CR2; //清除停止位(SR1第4位),先读SR1,再写CR2
nop();
I2C1->CR2 = Nuse;
}
if(I2C1->SR1&0X04) //BTF位,先读SR1,再读或者写DR寄存器清除
{
Nuse=I2C1->DR;
// I2C->DR=Nuse;
}
if(I2C1->SR1 & 0x40) //RXNE,数据寄存器是否为空,0空1非
{
//将收到的数据储存到RX数组中。虽然我们是从机,用不到这里的数据,但是这里必须读DR寄存器收取数据.不然会出错
Slave_Buffer_Rx[I2C_Rx_Idx++]=I2C1->DR;
if(I2C_Rx_Idx>=MAX_Id)
{
I2C_Rx_Idx=0;
}
}
if(I2C1->SR1 & 0x80) //将发送的数据放入DR寄存器,清除TXE
{
I2C1->DR = Slave_Buffer_Tx[I2C_Tx_Idx++];
if(I2C_Tx_Idx>=MAX_Id)I2C_Tx_Idx=0;
}

}

很多人都说硬件I2C不稳定,不好用,不过就我目前测试来讲,还是挺稳定好用的.

你可能感兴趣的:(STM8)