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)
	{
		

	}
}
相关推荐
发光小北4 小时前
单通道串口服务器如何应用?
运维·服务器·单片机
车载测试牛马笔记4 小时前
CAN一致性-物理层--高压通信范围测试
单片机·嵌入式硬件
国产电子元器件4 小时前
ACS770还能打吗?最近测试了一款国产霍尔电流传感器
嵌入式硬件
www.stcai.com4 小时前
《单片机原理与应用》,详细介绍。基于 STC, Ai8051U, 8位/32位 双核 全支持 AiCube 图形化配置外设,自动生成程序框架
单片机·嵌入式硬件
嵌入式老牛14 小时前
HC32F460_ADC驱动(三)
单片机·嵌入式硬件
秀秀更健康15 小时前
stm32: 系统时钟如何配置为72Mhz
stm32·单片机·嵌入式硬件
QAQ小菜鸟18 小时前
一、安装Altium Designer 26.1
嵌入式硬件
嵌入式-老费20 小时前
esp32开发与应用(用面包板去学模块)
嵌入式硬件
归零鸟20 小时前
WD Elements移动硬盘能识别出盘但不能出盘的修复记录
stm32·单片机·嵌入式硬件