4.4 I2C写数据
目录
1.I2C模块的内部结构框图
2.数据发送过程简介
3.等待总线空闲
4.发送起始位
5.发送地址
6.发送数据
7.发送停止位
8.oled屏幕地址0x78
9.代码
写数据也就是发送数据
主机(单片机)发送,从机接收数据
1.I2C模块的内部结构框图
2.数据发送过程简介
3.等待总线空闲
定义发送函数
4.发送起始位
给START位写1就代表发送
当SB标志位从0变成1的时候就代表起始位发送完成
5.发送地址
主机发送7位的从机地址+RW#位(表示方向),并检测是否寻址成功(要收到ACK才成功)
如果AF标志位等于1代表寻址失败,如果ADDR等于1代表寻址成功
因为后面要使用这两个标志位,所以要先对AF和ADDR做清0,又因为ADDR比较特殊,所以就先清除AF标志位。
注意下面的Addr是数据,不是标志位ADDR,注意区分。
发送的数据(Addr)与上0xfe就可以最后一位清0,那样就可以保证RW#位是0,因为RW#位0代表写,1代表读
清除ADDR:先读SR1,再读SR2
6.发送数据
在数据发送阶段,主机发送一个字节,从机回一个ACK,主机发送一个字节,从机回一个ACK
要写入数据到数据寄存器之前,要判断一下数据寄存器有没有值,要查一下TxE标志位和AF标志位
AF标志位是用来查看是否寻址失败了,也就是查看上一个数据有没有被接收,查看一下ACK,AF等于1代表上一个数据被拒收了,那就要停止数据发送,先发一个停止位,再返回一个-2代表数据拒收
TXE标志位如果等于1,代表数据寄存器是空了,就可以发送数据了
等待发送数据寄存器和移位数据寄存器为空,就代表发送完了
BTF标志位可以判断这两个寄存器是否为空
当BTF标志位从0变成1的时候就说明数据发送完成了
在这之前也查询一下AF标志位防止数据被拒收。
7.发送停止位
8.oled屏幕地址0x78
把下面几个字节发给oled屏就亮了
9.代码
#include "stm32f10x.h"void My_I2C_Init(void);
int My_I2C_SendBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pDate,uint16_t Size);int main(void)
{My_I2C_Init();uint8_t commands[]={0x00,0x8d,0x14,0xaf,0xa5};My_I2C_SendBytes(I2C1,0x78,commands,5);while(1){}
}void My_I2C_Init(void)
{//#1.IO引脚初始化//对I2C1进行重映射AFIORCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);//对PB8和PB9初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);// #2.初始化I2C1模块RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//这样I2C1引脚就复位了RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);I2C_InitTypeDef I2C_InitStruct;I2C_InitStruct.I2C_ClockSpeed=400000;//波特率400kI2C_InitStruct.I2C_Mode=I2C_Mode_I2C;//标准I2CI2C_InitStruct.I2C_DutyCycle=I2C_DutyCycle_2; //占空比2:1I2C_Init(I2C1,&I2C_InitStruct);I2C_Cmd(I2C1,ENABLE); //闭合I2C1的总开关
}int My_I2C_SendBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pDate,uint16_t Size)
{// #1. 等待总线空闲while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BUSY)== SET);// #2. 发送起始位I2C_GenerateSTART(I2Cx,ENABLE);while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_SB)==RESET);// #3. 寻址阶段I2C_ClearFlag(I2Cx,I2C_FLAG_AF);//AF标志位清除I2C_SendData(I2Cx, Addr & 0xfe);//等待从机呼应while(1){if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_ADDR)==SET) break;if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET){I2C_GenerateSTOP(I2Cx,ENABLE); //发送停止位return -1; //寻址失败}}//清除ADDR(先读SR1,再读SR2)I2C_ReadRegister(I2Cx,I2C_Register_SR1);I2C_ReadRegister(I2Cx,I2C_Register_SR2);// #4.1 发送数据for(uint16_t i=0;i<Size;i++){while(1){if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET){I2C_GenerateSTOP(I2Cx,ENABLE);return -2;}if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_TXE)==SET) break; }I2C_SendData(I2Cx,pDate[i]);}// 等待发送完成while(1){if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET){I2C_GenerateSTOP(I2Cx,ENABLE);return -2;}if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BTF)==SET) break; }// # 5. 发送停止位I2C_GenerateSTOP(I2Cx,ENABLE);return 0; //成功}