STM32F1 GPIO模拟I2C
gpio:
通用输入输出端口的简称,也就是stm32可控制的引脚,stm32芯片的GPIO分为很多组,每组16个引脚。stm32f103vet的芯片共有5组GPIO引脚
一、gpio工作模式
输入模式:
-
模拟输入:输入模拟信号,用于ADC采集。
-
浮空输入:输入不确定,完全由外部的输入决定。
-
上拉输入:默认电平由上拉电阻决定
-
下拉输入:默认电平由下拉电阻决定
输出模式:
-
开漏输出:上方的P-MOS管完全不工作(关闭),如果控制输出为0,低电平,则N-MOS管导通,使输出接地。如果控制输出为1,高电平,则N-MOS管关闭,所以引脚既不输出高电平,也不输出低电平,为高阻态。
-
推免输出:输出寄存器上的’0’**N-MOS,为低电平。而输出寄存器上的’1’将**P-MOS,为高电平。
复用模式:输出数据寄存器无效,输出信号源来自其他外设,可工作在推免及开漏模式。输入有效
-
复用开漏输出:
-
复用推免输出:
二、BERR寄存器:
i2c协议:
I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。
I2C通信方式为半双工,只有一根SDA线,同一时间只可以单向通信,
I2C物理通信特点:
(1)它是一个支持设备的总线。在一个I2C通信总线中,可连接多个I2C通信设备,支持多个通信主机及多个通信从机。
(2)一个I2C总线只使用两条线路,一条双向串行数据线(SDA),一条串行时钟线(SCL)
(3)每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
(4)总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当设备都空闲时,都输出高阻态时,有上拉电阻吧总线拉成高电平。
(5)多个主机同时使用总线时,为防止数据冲突,会利用仲裁的方式决定由那个设备占用总线。
I2C协议层
(6)具有三种传输模式:标准模式传输效率为100kbps,快速模式为400kbps,高速模式可达3.4Mbps。
I2C的协议定义了通信的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播环节等。
起始和停止信号:
起始(S):当SCL线是高电平时,SDA线从高电平向低电平切换。
停止(P):当SCL线是高电平时,SDA线从低电平向高电平切换。
数据有效性:
SDA数据线在SCL的每个时钟周期只传输一位数据。传输时,SCL为高电平时SDA表示的数据有效,及此时,SDA为高电平时表示数据为“1”,为低电平是表示的数据为“0”。当SCL为低电平时,SDA数据无效。
地址及数据方向:
I2C协议规定设备的地址可以是7位或10位,实际总7位的地址应用比较广泛。紧跟设备地址的一个数据位用来表示数据传输方向。数据方向位为“1”时表示主机读从机数据,为“0”时,表示主机向从机写数据。
读数据时主机会释放SDA信号线的控制,有从机控制SDA线
响应:
I2C数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”
应答信号(ACK):SCL为高电平时,SDA为低电平.
非应答信号(NACK):SCL为低电平时,SDA为高电平.
iic发送/接收数据
数据包以8bit为单位进行发送/接收,每发送/接收一个字节的数据,主机必需发送/接收到一个应答信号才能进行下一步操作。当SCL为高电平时,主机发送/接收数据,为低时,根据要发送的数据改变SDA输出。
gpio模拟iic
//iic_sht20.h #include "stm32f1xx_hal.h"
#define GPIO_PORT_I2C GPIOA // GPIO端口 #define I2C_SCL_PIN SCL_Pin // 连接到SCL时钟线的GPIO #define I2C_SDA_PIN SDA_Pin // 连接到SDA数据线的GPIO #define W_ADDR 0X81 #define R_ADDR 0X80
/*commnd*/ #define T_HOST_MEASURE 0xe3u #define RH_HOST_MEASURE 0xe5u #define T_NOHOST_MEASURE 0xf3u #define RH_NOHOST_MEASURE 0xf5u #define WRITE_USER_REG 0xd6u #define READ_USER_REG 0xd7u #define RESET 0xfeu /* 定义读写SCL和SDA的宏 */ #define I2C_SCL_1() GPIO_PORT_I2C->BSRR = I2C_SCL_PIN // SCL = 1 #define I2C_SCL_0() GPIO_PORT_I2C->BSRR = (uint32_t)I2C_SCL_PIN << 16U // SCL = 0
#define I2C_SDA_1() GPIO_PORT_I2C->BSRR=I2C_SDA_PIN // SDA = 1 #define I2C_SDA_0() GPIO_PORT_I2C->BSRR = (uint32_t)I2C_SDA_PIN << 16U // SDA = 0
void i2c_Start(void); void i2c_Stop(void); void i2c_Ack(void); void i2c_SendByte(uint8_t _ucByte); uint8_t i2c_ReadByte(void); uint8_t i2c_WaitAck(void); GPIO_PinState I2C_SDA_READ(void); GPIO_PinState I2C_SCL_READ(void); uint8_t i2c_CheckDevice(uint8_t _Address); uint8_t sht20_send_cmd(uint8_t cmd, uint8_t *buf, uint8_t size); |
//iic_sht20.c #include "iic_sht20.h" static void i2c_Delay(void) { uint8_t i,j; for (i = 0; i < 255; i++) { for(j=0;j<255;j++); } }
GPIO_PinState I2C_SDA_READ(void) { return HAL_GPIO_ReadPin(GPIOA,SDA_Pin); } GPIO_PinState I2C_SCL_READ(void) { return HAL_GPIO_ReadPin(GPIOA,SCL_Pin); } /*** *主机发送数据,需要设置为开漏输出模式 选用推免输出可能会产生很大的电流,烧毁器件, 而*开漏输出不接上拉电阻不能输出高电平,加上拉电阻时电流由上拉电阻决定,因此使用开漏输出 */ void set_mode_out(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void set_mode_in(void) //接收数据,需要设置为浮空输入 { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); }
void i2c_Start(void)// 开始信号,当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号
I2C_SDA_1(); I2C_SCL_1(); i2c_Delay(); I2C_SDA_0(); i2c_Delay(); I2C_SCL_0(); i2c_Delay(); }
void i2c_Stop(void) { I2C_SDA_0(); I2C_SCL_1(); i2c_Delay(); I2C_SDA_1(); i2c_Delay(); } void i2c_SendByte(uint8_t _ucByte) { uint8_t i; for (i = 0; i < 8; i++) { if (_ucByte & 0x80) { I2C_SDA_1(); } else { I2C_SDA_0(); } i2c_Delay(); I2C_SCL_1(); i2c_Delay(); I2C_SCL_0(); if (i == 7) { I2C_SDA_1(); // 释放总线 } _ucByte <<= 1; i2c_Delay(); } } uint8_t i2c_ReadByte(void) { uint8_t i; uint8_t value; set_mode_in(); value = 0; for (i = 0; i < 8; i++) { value <<= 1; I2C_SCL_1(); if (GPIO_PIN_SET==I2C_SDA_READ()) { value|=0x01; } I2C_SCL_0(); i2c_Delay(); } set_mode_out(); return value; }
uint8_t i2c_WaitAck(void) { uint8_t re;
I2C_SDA_1(); /* CPU释放SDA总线 */ set_mode_in(); I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */ i2c_Delay(); if (GPIO_PIN_SET==HAL_GPIO_ReadPin(GPIOA,I2C_SDA_PIN)) { re = 1; } else { re = 0; } I2C_SCL_0(); i2c_Delay(); set_mode_out(); return re; }
void i2c_Ack(void) { set_mode_out(); I2C_SDA_0(); /* CPU驱动SDA = 0 */ I2C_SCL_1(); i2c_Delay(); I2C_SCL_0(); i2c_Delay(); }
void i2c_NAck(void) { I2C_SDA_1(); /* CPU驱动SDA = 1 */ i2c_Delay(); I2C_SCL_1(); i2c_Delay(); I2C_SCL_0(); i2c_Delay(); }
uint8_t i2c_CheckDevice(uint8_t _Address) { uint8_t ucAck; i2c_Stop(); if(I2C_SDA_READ() && I2C_SCL_READ()) { i2c_Start(); i2c_SendByte(_Address); ucAck = i2c_WaitAck(); i2c_Stop(); return ucAck; } return 2; /* I2C总线异常 */ }
/* */ uint8_t Measure(uint8_t *buf,uint8_t cmd,uint8_t size) { if(size<3) /*两个数据位和一个校验位*/ { return 1; } i2c_Stop(); i2c_Start(); i2c_SendByte(0x80); if(i2c_WaitAck()!=0) { return 1; } i2c_SendByte(cmd); //测量命令 if(i2c_WaitAck()!=0) { return 2; } i2c_Start(); i2c_SendByte(0x81); if(i2c_WaitAck()!=0) { return 3; } HAL_Delay(66); //sht20测量延时 buf[0]=i2c_ReadByte(); i2c_Ack(); buf[1]=i2c_ReadByte(); i2c_Ack(); buf[2]=i2c_ReadByte(); i2c_NAck(); i2c_Stop();
return 0; } |