stm32--I2C

一个串口只能连接一个串口设备,,,不能连接多个设备,,,

I2C : 一个接口可以连接多个设备,,,


不连接默认就是高电平


占空比: 在一个周期里面,,高电压占整个周期的比例

duty cycle

stm32内部的每个外设,,,USART还是I2C 本质都是一堆寄存器+状态机

外设复位就是: 把这些寄存器恢复默认值

c 复制代码
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
	
	// 开启 复位信号
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);
	// 关闭复位信号 ,,, 硬件的复位,,本质是一个脉冲,,,是一个短暂的高电平
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);

SR: status register : 状态寄存器

SR : status register : 状态寄存器

busy : 总线忙标志位 1:总线忙 0:总线空闲

  • SB : start bit : 起始位标志,, 当成功发送出起始条件 start condition 后,,硬件会将此置为1
  • BUSY : bus busy ,, 总线忙碌标志,,当I2C外设正在进行数据传输时(总线上有通信活动时),,会置为1,,用于判断总线是否被其他设备占用
  • AF : acknowledge failure : 应答失败标志,,, 本地发送完一个字节后,没有收到从机返回的ack,,置为1
  • ADDR :address sent / matched : 地址标志,,,1:寻址成功 ,,,
  • TxE : transmit data register empty : 发送数据寄存器空标志
  • BTF: byte transfer finished : 字节传输完成标志
复制代码
0x00  

电荷泵 : 小型升压器 ,,将3.3V 变成7v,8v,10v


oled像素发光时,,需要比较高的驱动电压,,但是stm32只给3.3不够,必须自己升压

电荷泵 : 一点一点把电荷 搬高,,所以叫 电荷泵,, charge pump

oled里面空间很小,,放不下变压器,,所以他用的是 电容+开关 来升压

SSD1306 规定:

  • 0x00 : 后面是命令
  • 0x40 : 后面是显示数据
  • 0x8D : 要干什么
  • 0x14 : 开启电荷泵
  • 0x10 : 关闭电荷泵
  • 0xAF : 打开显示器
  • 0xAE : 黑屏
  • 0xA5 : 全屏点亮 : 无视显存,,所有像素全亮

点亮屏幕:

c 复制代码
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "lightSensor.h"
#include "stdio.h"


int my_i2c_sendBytes(I2C_TypeDef* i2cx,uint8_t addr,uint8_t *pData,uint16_t size){
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	
	
	// 等待总线空闲
	while(I2C_GetFlagStatus(i2cx,I2C_FLAG_BUSY) == SET);
	
	// 发送起始位
	I2C_GenerateSTART(i2cx,ENABLE);
	
	// start bit  等待起始标志位发出去
	while(I2C_GetFlagStatus(i2cx,I2C_FLAG_SB) == RESET);
	
	
	 // AF是错误标志,必须软件手动清零
	// 清除 acknowledge failure
	I2C_ClearFlag(i2cx,I2C_FLAG_AF);
	
	
	// 发送地址 ,, 向从机写数据
	I2C_SendData(i2cx,addr & 0xfe);
	
	
	// 等待从机ack
	while(1){
		
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_AF) == SET){
			// ack失败
			I2C_GenerateSTOP(i2cx,ENABLE);
			return -1; // 寻址失败
		}
		
		
		// 寻址成功,,跳到下面发送数据
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_ADDR) == SET){
			break;
		}
		
		
	}
	
	// 清除ADDR标志位,,,把 status register 读一遍,,就自动清零了ADDR
	I2C_ReadRegister(i2cx,I2C_Register_SR1);
	I2C_ReadRegister(i2cx,I2C_Register_SR2);
	
	
	// 发送数据
	for(uint16_t i=0;i<size;i++){
		// 查看上一个数据是否被拒收  AF : acknowledge failure
		// 查看上一个数据是否发送完了 txe : transmit data register empty
		
		while(1){
			if(I2C_GetFlagStatus(i2cx,I2C_FLAG_AF) == SET){
				// ack失败
				I2C_GenerateSTOP(I2C1,ENABLE);
				return -2; // 数据被拒收
			}
		
			if(I2C_GetFlagStatus(i2cx,I2C_FLAG_TXE) == SET){
				break;
			}
		}
		
		// 写数据
		I2C_SendData(i2cx,pData[i]);
	}
	
	
	
	// 发完之后判断数据是否发送完成
	while(1){
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_AF) == SET){
			// ack失败
			I2C_GenerateSTOP(i2cx,ENABLE);
			return -2; // 数据被拒收
		}
	
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_BTF) == SET){
			break;
		}
	}
	
	
	
	// 发送停止位
	I2C_GenerateSTOP(i2cx,ENABLE);
	return 0; // 成功
	
}



/**
板载led初始化
*/
void my_onBoardLED_init(){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	GPIO_InitTypeDef GPIO_initStruct = {0};
	
	// 操控引脚 
	GPIO_initStruct.GPIO_Pin = GPIO_Pin_13;
	
	// 板载led ,,是开漏输出
	GPIO_initStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_initStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	// 初始化IO引脚,,
	GPIO_Init(GPIOC,&GPIO_initStruct);
	
	
	GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}


void my_oled_init(){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_initStruct = {0};
	
	// 操控引脚 
	GPIO_initStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	

	// 必须是开漏,,多设备共用一根线,,如果多个人输出高,,就短路了,,I2C规定只能拉低
	GPIO_initStruct.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_initStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	// 初始化IO引脚,,
	GPIO_Init(GPIOB,&GPIO_initStruct);
	
	

}





uint8_t keyNum;
uint8_t lightSensorFlag;
int main(void)
{
	
	my_oled_init();
	// 

	// 开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
	
	// 开启复位信号
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);
	
	// 关闭复位信号  ,,, 硬件的复位本质上是一个脉冲
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);
	
	
	
	I2C_InitTypeDef i2c_initStruct;
	
	i2c_initStruct.I2C_ClockSpeed = 400000;
	
	i2c_initStruct.I2C_Mode = I2C_Mode_I2C;
	
	i2c_initStruct.I2C_DutyCycle = I2C_DutyCycle_2;
	
	I2C_Init(I2C1,&i2c_initStruct);
	
	
	// 闭合I2C1的总开关
	I2C_Cmd(I2C1,ENABLE);
	
	
	uint8_t commands[] = {0x00,0x8d,0x14,0xaf,0xa5};
	
	my_i2c_sendBytes(I2C1,0x78,commands,5);
	
	
	while(1)
	{
		

	}
}

stm32的 i2c 外设被 卡在了地址阶段,,只有清除了 ADDR (address sent and acknowledged),,,不清除不允许进入发送数据阶段,,I2C硬件会认为,,软件还没处理地址阶段

TXE : transmit data register empty

stm32的I2C外设,,, 内部会自动ack,,默认情况下,,每收到一个字节,,硬件都会自动ack,,

将ack禁用之后,默认是开漏输出,,高阻态,,不回复ack,就相当于NACK

stop之后不是立即生效的,,所以也需要设置nack

RXNE : receive data register not empty

i2c读取数据:

c 复制代码
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "lightSensor.h"
#include "stdio.h"


int my_i2c_sendBytes(I2C_TypeDef* i2cx,uint8_t addr,uint8_t *pData,uint16_t size){
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	
	
	// 等待总线空闲
	while(I2C_GetFlagStatus(i2cx,I2C_FLAG_BUSY) == SET);
	
	// 发送起始位
	I2C_GenerateSTART(i2cx,ENABLE);
	
	// start bit  等待起始标志位发出去
	while(I2C_GetFlagStatus(i2cx,I2C_FLAG_SB) == RESET);
	
	
	 // AF是错误标志,必须软件手动清零
	// 清除 acknowledge failure
	I2C_ClearFlag(i2cx,I2C_FLAG_AF);
	
	
	// 发送地址 ,, 向从机写数据
	I2C_SendData(i2cx,addr & 0xfe);
	
	
	// 等待从机ack
	while(1){
		
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_AF) == SET){
			// ack失败
			I2C_GenerateSTOP(i2cx,ENABLE);
			return -1; // 寻址失败
		}
		
		
		// 寻址成功,,跳到下面发送数据
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_ADDR) == SET){
			break;
		}
		
		
	}
	
	// 清除ADDR标志位,,,把 status register 读一遍,,就自动清零了ADDR
	I2C_ReadRegister(i2cx,I2C_Register_SR1);
	I2C_ReadRegister(i2cx,I2C_Register_SR2);
	
	
	// 发送数据
	for(uint16_t i=0;i<size;i++){
		// 查看上一个数据是否被拒收  AF : acknowledge failure
		// 查看上一个数据是否发送完了 txe : transmit data register empty
		
		while(1){
			if(I2C_GetFlagStatus(i2cx,I2C_FLAG_AF) == SET){
				// ack失败
				I2C_GenerateSTOP(I2C1,ENABLE);
				return -2; // 数据被拒收
			}
		
			if(I2C_GetFlagStatus(i2cx,I2C_FLAG_TXE) == SET){
				break;
			}
		}
		
		// 写数据
		I2C_SendData(i2cx,pData[i]);
	}
	
	
	
	// 发完之后判断数据是否发送完成
	while(1){
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_AF) == SET){
			// ack失败
			I2C_GenerateSTOP(i2cx,ENABLE);
			return -2; // 数据被拒收
		}
	
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_BTF) == SET){
			break;
		}
	}
	
	
	
	// 发送停止位
	I2C_GenerateSTOP(i2cx,ENABLE);
	return 0; // 成功
	
}



/**
板载led初始化
*/
void my_onBoardLED_init(){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	GPIO_InitTypeDef GPIO_initStruct = {0};
	
	// 操控引脚 
	GPIO_initStruct.GPIO_Pin = GPIO_Pin_13;
	
	// 板载led ,,是开漏输出
	GPIO_initStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_initStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	// 初始化IO引脚,,
	GPIO_Init(GPIOC,&GPIO_initStruct);
	
	
	GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}


void my_oled_init(){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_initStruct = {0};
	
	// 操控引脚 
	GPIO_initStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	

	// 必须是开漏,,多设备共用一根线,,如果多个人输出高,,就短路了,,I2C规定只能拉低
	GPIO_initStruct.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_initStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	// 初始化IO引脚,,
	GPIO_Init(GPIOB,&GPIO_initStruct);
	
	
	
	
	
		// 开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
	
	// 开启复位信号
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);
	
	// 关闭复位信号  ,,, 硬件的复位本质上是一个脉冲
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);
	
	
	
	I2C_InitTypeDef i2c_initStruct;
	
	i2c_initStruct.I2C_ClockSpeed = 400000;
	
	i2c_initStruct.I2C_Mode = I2C_Mode_I2C;
	
	i2c_initStruct.I2C_DutyCycle = I2C_DutyCycle_2;
	
	I2C_Init(I2C1,&i2c_initStruct);
	
	
	// 闭合I2C1的总开关
	I2C_Cmd(I2C1,ENABLE);

}



/**
接收从机数据
*/
int my_i2c_receiveBytes(I2C_TypeDef* i2cx,uint8_t addr,uint8_t *pBuffer,uint16_t size){
	
	// 从机发送,, 主机接收
	// 发送起始位
	I2C_GenerateSTART(i2cx,ENABLE);
	
	// start bit  等待起始标志位发出去
	while(I2C_GetFlagStatus(i2cx,I2C_FLAG_SB) == RESET);
	
	
	// 清除 acknowledge failure
	I2C_ClearFlag(i2cx,I2C_FLAG_AF);
	
	// 发送地址 ,, 向从机读数据
	I2C_SendData(i2cx,addr | 0x01);
	
	while(1){
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_AF) == SET){
			// ack失败
			I2C_GenerateSTOP(i2cx,ENABLE);
			return -1; // 寻址失败
		}
		
			// 寻址成功,,跳到下面发送数据
		if(I2C_GetFlagStatus(i2cx,I2C_FLAG_ADDR) == SET){
			break;
		}
	}
	
	
	// 从从机读数据
	if(size ==1){
		// 只接收1个字节
			
		// 清除ADDR
			I2C_ReadRegister(i2cx,I2C_Register_SR1);
			I2C_ReadRegister(i2cx,I2C_Register_SR2);
		
		
		// 最后一个字节必须设置 NACK 和 stop
		I2C_AcknowledgeConfig(i2cx,DISABLE);
		I2C_GenerateSTOP(i2cx,ENABLE);
		
		while(I2C_GetFlagStatus(i2cx,I2C_FLAG_RXNE) == RESET);
	
		// 接收到数据
		pBuffer[0] = I2C_ReceiveData(i2cx);
	}
	
		// 接收两个字节
	if(size == 2){
		// 清除ADDR
			I2C_ReadRegister(i2cx,I2C_Register_SR1);
			I2C_ReadRegister(i2cx,I2C_Register_SR2);
		
		
		// 第一个字节  ack为1
		I2C_AcknowledgeConfig(i2cx,ENABLE);
		
		// 等待第一个字节接收完成
		while(I2C_GetFlagStatus(i2cx,I2C_FLAG_RXNE) == RESET);
		pBuffer[0] = I2C_ReceiveData(i2cx);
		
		
		// 设置ack为0,和stop
		// 最后一个字节必须设置 NACK 和 stop
		I2C_AcknowledgeConfig(i2cx,DISABLE);
		I2C_GenerateSTOP(i2cx,ENABLE);
		
		
		while(I2C_GetFlagStatus(i2cx,I2C_FLAG_RXNE) == RESET);
		pBuffer[1] = I2C_ReceiveData(i2cx);
			
	}
	
	
	if(size >  2){
			// 清除ADDR
			I2C_ReadRegister(i2cx,I2C_Register_SR1);
			I2C_ReadRegister(i2cx,I2C_Register_SR2);
		
			I2C_AcknowledgeConfig(i2cx,ENABLE);
		
		for(uint8_t i=0;i<size;i++){
		
			if(i==size-1){
					// 最后一个字节必须设置 NACK 和 stop
					I2C_AcknowledgeConfig(i2cx,DISABLE);
					I2C_GenerateSTOP(i2cx,ENABLE);
			
			}
			
			while(I2C_GetFlagStatus(i2cx,I2C_FLAG_RXNE) == RESET);
			pBuffer[i] = I2C_ReceiveData(i2cx);
		
				
		}
	
	}

	
	return 0;

}


uint8_t keyNum;
uint8_t lightSensorFlag;
int main(void)
{
	
	
	my_onBoardLED_init();
	my_oled_init();
	// 


	
//	
	uint8_t commands[] = {0x00,0x8d,0x14,0xaf,0xa5};
	
	my_i2c_sendBytes(I2C1,0x78,commands,5);
//	
	
	uint8_t rcvd;
	my_i2c_receiveBytes(I2C1,0x78,&rcvd,1);
	
	
	if((rcvd & (0x01 << 6)) == 0){
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
	}else{
	
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
	}
	
	

	
	
	while(1)
	{
		

	}
}
相关推荐
国科安芯3 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
茯苓gao5 小时前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
梁朝辉7 小时前
STM32H750VBT6中ADCINP与INN什么区别
stm32·单片机·嵌入式硬件
省四收割者8 小时前
一文详解信号完整性(1)
python·嵌入式硬件·数学建模·信息与通信·信号处理·智能硬件
崇山峻岭之间9 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious10 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
KaifuZeng14 小时前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计15 小时前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
kebidaixu15 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片详解
stm32
kebidaixu16 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片调试实录:四个关键问题与解决方案
stm32