【极海APM32F4xx Tiny】极海APM32F407__IIC_STH30(2)

尹小舟   2023-7-5 13:29 楼主

前言

       本贴我们将向大家介绍如何利用 APM32F407的普通 IO 口模拟 IIC 时序,并实现和 STH30之 间的通信。在本贴中,我们将利用 APM32F407的普通 IO 口模拟 IIC 时序,来实现 SHT30的 寄存器的读写,并将读取SHT30采集到的温湿度数据,通过串口打印到在 电脑上。本文分为如下几个部分:

  •             SHT30介绍
  •             IIC协议介绍
  •             数据打印软件设计
  •            下载验证

SHT30介绍

      sht30是一个瑞士企业盛世瑞恩生产的温湿度传感器,从sht10到sht31,盛世瑞恩的传感器还是不错了。

          利用I2C进行数据传输,具有两个可选地址,宽电源电压从2.4V到5.5V。

          sht30最高支持1000k的传输速率。因此通讯时间非常短。

          sht30有两种读取数值的方法,我介绍一下使用iic的状态查询和数值查询方法。

  •            1:状态查询

                           发送指令:0xF32D. image.png

  •         2:数值查询。

                       发送指令0x2C06

                         在这种模式下,发出测量命令触发一个数据对的获取。每个数据对由一个16位温度和一个16位组成湿度值(按此顺序)。

                 在传输过程中data值后面总是跟一个CRC校验和。

                       通过iic发送完毕之后,sht30返回的数值是6个字节的数组。

                             1[温度高八位]    2[温度第八位]    3[温度crc校验]

                             4[湿度高八位]    5[湿度第八位]    6[湿度crc校验]

 IIC协议介绍

  •      I2C 物理层

image.png  

 

物理层特点:

  1. 支持多设备的总线(多个设备共用的信号线),在 I2C 通信总线中,可连 接多个通信主机与通信从机。
  2. 一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA),一条 串行时钟线(SCL)。数据线用于发送数据,时钟线用于数据收发同步。
  3. 每一个连接在总线上的设备都具有独立的地址(七位或十位),主机根据 设备地址寻址访问从设备。
  4. 总线需要接上拉电阻到电源,I2C 总线空闲状态下,输出为高阻态,所有 设备空闲时,都输出高阻态,上拉电阻把总线拉成高电平。
  5. 三种通信模式:标准模式(高达 100KHz) 快速模式(高达 400KHz)
  6. 超快速模式(高达 1MHz)。
  7. 多主机同时使用总线时,防止数据冲突,采取总线仲裁方式决定哪个设备 占用总线。
  8. 可编程建立和保持时间,可对 I2C 中 SCL 的高电平时间与低电平时间进 行编程。

协议层特点:

 

  • 数据以帧的形式发送,每一帧中由 1 个字节(8 位)组成。
  • 在 SCL 的上升沿阶段,SDA 需要保持稳定,SDA 在 SCL 为低期间作出改 变。
  • 除了数据帧,I2C 总线还有起始信号,停止信号,应答信号。
    •  起始位:在 SCL 为稳定的高电平期间,SDA 的一个下降沿启动发送。
    •  停止位:在 SCL 为稳定的高电平期间,SDA 的一个上升沿停止发送。
    •  应答位:用于表示一个字节发送成功。总线发送器(无论主机还是从机),
  • 在发送 8 个位的数据后,SDA 将释放(由输出变为输入),在第九个时钟 脉冲期间,接收器将 SDA 拉低,来应答接收到了数据

 

IIC通讯过程

image.png     image.png

(1) 灰色此数据由主机发送到从机

(2) S:起始信号

(3) SLAVE ADDRESS:从机地址

(4) 白色此数据由从机发送到主机

(5) R/W :发送方向选择位 1 为读取 0 为写入

(6) P:停止信号

数据打印软件设计

        

软件延时


#include "apm32f4xx.h"
#include "SysTick_Delay.h"

/**@} end of group SysTick_TimeBase_Macros*/

static __IO u32 TimingDelay;

/*!
 * @brief        Start SysTick
 *
 * @param       None
 *
 * @retval      None
 */
void SysTick_Init(void)
{
    /** SystemFrequency / 1000 = 1ms */
    if (SysTick_Config(SystemCoreClock / 1000))
    {
        /** Capture error */
        while (1);
    }
}

/*!
 * @brief       Precise Delay
 *
 * @param       nTime in milliseconds
 *
 * @retval      None
 */
void SysTick_Delay_ms(__IO u32 nTime)
{
    TimingDelay = nTime;
    while(TimingDelay != 0);
}



/*!
 * @brief       Precise Delay
 *
 * @param       nTime in milliseconds
 *
 * @retval      None
 */
void SysTick_Delay_us(__IO u32 nTime)
{
	  SysTick_Config(SystemCoreClock / 1000000);
    TimingDelay = nTime;
    while(TimingDelay != 0);
	  SysTick_Config(SystemCoreClock / 1000);
}

/*!
 * @brief       Decrements the TimingDelay
 *
 * @param       None
 *
 * @retval      None
 */
void TimingDelay_Decrement(void)
{
    if(TimingDelay != 0)
    {
        TimingDelay--;
    }
}

 

 

SysTick_Config (SystemCoreClock/1000) ; 这个函数打开了SysTick的中断,同时也设置了Systick的重装载寄存器。 SystemCoreClock/1000既是系通时钟频率的千分之一。 也就是说没每秒钟,Systick寄存器会装满1000次,每次1ms,SystemCoreClock / 1000000每次就是1us,1us Systick中断太频烦延时之后恢复1ms.

 

 

GPIO模拟IIC

 

#ifndef __I2C_H
#define __I2C_H			 



#include "apm32f4xx.h"
#include "SysTick_Delay.h"
#include "apm32f4xx_gpio.h"

#include "apm32f4xx_rcm.h"
/**********************
引脚别名定义
***********************/			




#define I2C_PageSize  8  


#define I2C_SCL_GPIO_PIN            GPIO_PIN_6
#define I2C_SCL_GPIO_PORT           GPIOB
#define I2C_SCL_GPIO_CLK            RCM_AHB1_PERIPH_GPIOB
#define I2C_SCL_SOURCE              GPIO_PIN_SOURCE_6

#define I2C_SDA_GPIO_PIN            GPIO_PIN_7
#define I2C_SDA_GPIO_PORT           GPIOB
#define I2C_SDA_GPIO_CLK            RCM_AHB1_PERIPH_GPIOB
#define I2C_SDA_SOURCE              GPIO_PIN_SOURCE_7




#define I2C_SDA_IN()               { I2C_SDA_GPIO_PORT->MODE &= ~(7 << (3 * 2)); I2C_SDA_GPIO_PORT->MODE |= 0 << 7 * 2; }
#define I2C_SDA_OUT()              { I2C_SDA_GPIO_PORT->MODE &= ~(7 << (3 * 2)); I2C_SDA_GPIO_PORT->MODE |= 1 << 7 * 2; }

#define I2C_SCL_1                   GPIO_SetBit(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN)
#define I2C_SCL_0                   GPIO_ResetBit(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN)

#define I2C_SDA_1                   GPIO_SetBit(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN)
#define I2C_SDA_0                   GPIO_ResetBit(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN)

#define I2C_SDA_INPUT               GPIO_ReadInputBit(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN)



/* Private function prototypes -----------------------------------------------*/

void GPIO_Simulation_IIC_Config(void);
void I2C_delay(void);
unsigned char I2C_Start(void);
void I2C_Stop(void);
void I2C_Ack(void);
void I2C_NoAck(void);
unsigned char I2C_WaitAck(void) ;
void I2C_SendByte(unsigned char SendByte) ;
unsigned char I2C_ReceiveByte(void)  ;


#endif 
/*********************************************************************************************************
      END FILE
*********************************************************************************************************/

 

 

 


//头文件

#include "i2c.h"


void GPIO_Simulation_IIC_Config(void)
{
	  GPIO_Config_T     gpioConfigStruct;
    
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);
   
    
    gpioConfigStruct.pin = GPIO_PIN_6|GPIO_PIN_7;
    gpioConfigStruct.mode = GPIO_MODE_OUT;
    gpioConfigStruct.speed = GPIO_SPEED_50MHz;
    gpioConfigStruct.otype = GPIO_OTYPE_OD;
    gpioConfigStruct.pupd = GPIO_PUPD_NOPULL;
    GPIO_Config(GPIOB, &gpioConfigStruct);
    
    
    I2C_SCL_1;
    I2C_SDA_1;
		

}

 /**
  * @File    I2C_delay
  * @brief  延迟时间
  * @param  无
  * @retval 无
  */
void I2C_delay(void)  //for 1T  STC	delay 
{	
	SysTick_Delay_us(5);
}

 /**
  * @file   I2C_Start
  * @brief  起始信号
  * @param  无
  * @retval 无
  */
unsigned char I2C_Start(void)
{
	I2C_SDA_1;
	I2C_SCL_1; 
	I2C_delay();
	
	I2C_SDA_IN();
	if(!I2C_SDA_INPUT)return 0;	/* SDA线为低电平则总线忙,退出 */
	I2C_SDA_OUT();
	
	I2C_SDA_0;
	I2C_delay();
	
	I2C_SDA_IN();
	if(I2C_SDA_INPUT) return 0;	/* SDA线为高电平则总线出错,退出 */
	I2C_SDA_OUT();
	
	I2C_SDA_0;
	I2C_delay();
	return 1;
}

 /**
  * @file   I2C_Stop
  * @brief  停止信号
  * @param  无
  * @retval 无
  */
void I2C_Stop(void)
{
	I2C_SCL_0;
	I2C_delay();
	I2C_SDA_0;
	I2C_delay();
	I2C_SCL_1; 
	I2C_delay();
	I2C_SDA_1;
	I2C_delay();
}

 /**
  * @file   I2C_Ack
  * @brief  应答信号
  * @param  无
  * @retval 无
  */
void I2C_Ack(void)
{	
	I2C_SCL_0;
	I2C_delay();
	I2C_SDA_0;
	I2C_delay();
	I2C_SCL_1; 
	I2C_delay();
	I2C_SCL_0;
	I2C_delay();
}

 /**
  * @file   I2C_NoAck
  * @brief  无应答信号
  * @param  无
  * @retval 无
  */
void I2C_NoAck(void)
{	
	I2C_SCL_0;
	I2C_delay();
	I2C_SDA_1;
	I2C_delay();
	I2C_SCL_1; 
	I2C_delay();
	I2C_SCL_0;
	I2C_delay();
}

 /**
  * @file   I2C_WaitAck
  * @brief  等待Ack
  * @param  无
  * @retval 返回为:=1有ACK,=0无ACK
  */
unsigned char I2C_WaitAck(void) 	
{
	I2C_SCL_0;
	I2C_delay();
	I2C_SDA_1;			
	I2C_delay();
	I2C_SCL_1; 
	I2C_delay();
	
	I2C_SDA_IN();
	if(I2C_SDA_INPUT)
	{
      I2C_SCL_0;
      return 0;
	}
	I2C_SDA_OUT();
	
	I2C_SCL_0;
	return 1;
}

 /**
  * @file   I2C_SendByte
  * @brief  数据从高位到低位
  * @param  - SendByte: 发送的数据
  * @retval 无
  */
void I2C_SendByte(unsigned char SendByte) 
{
    unsigned char i=8;
    while(i--)
    {
			I2C_SCL_0;
			I2C_delay();
			if(SendByte&0x80)
			I2C_SDA_1;  
			else 
			I2C_SDA_0;   
			SendByte<<=1;
			I2C_delay();
			I2C_SCL_1; 
			I2C_delay();
    }
    I2C_SCL_0;
}


 /**
  * @file   I2C_ReceiveByte
  * @brief  数据从高位到低位
  * @param  无
  * @retval I2C总线返回的数据
  */
unsigned char I2C_ReceiveByte(void)  
{ 
    unsigned char i=8;
    unsigned char ReceiveByte=0;

    I2C_SDA_1;
		
		I2C_SDA_IN();
    while(i--)
    {
      ReceiveByte<<=1;      
      I2C_SCL_0;
      I2C_delay();
	    I2C_SCL_1; 
      I2C_delay();	
      if(I2C_SDA_INPUT)
      {
        ReceiveByte|=0x01;
      }
    }
		I2C_SDA_OUT();
		
    I2C_SCL_0;
    return ReceiveByte;
} 
   
 


/*********************************************************************************************************
      END FILE
*********************************************************************************************************/

 

主函数实现

 

 

/*!
 * @file        main.c
 *
 * @brief       Main program body
 *
 * @version      V1.0.2
 *
 * @date         2023-03-01
 *
 * @attention
 *
 *  Copyright (C) 2021-2023 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* Includes */

#include "main.h"
#include "Board.h"
#include "SysTick_Delay.h"
#include "stdio.h"
#include "stdarg.h"
#include "SHT30.h"

/* printf function configs to USART1*/
#define DEBUG_USART  USART1

#define LED2(x)        GPIO_WriteBitValue(GPIOE, GPIO_PIN_5,x)
#define LED3(x)        GPIO_WriteBitValue(GPIOE, GPIO_PIN_6,x)

#define KEY1           GPIO_ReadInputBit(GPIOC,GPIO_PIN_10)
#define KEY2           GPIO_ReadInputBit(GPIOC,GPIO_PIN_11)

 USART_Config_T usartConfigStruct;    //串口配置结构体




void  LED_GPIO_Config(void)
{
    GPIO_Config_T  configStruct;

    /** 开启GPIO_LED时钟 */
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);

    /** 配置GPIO_LED引脚 */
    GPIO_ConfigStructInit(&configStruct);
    configStruct.pin = GPIO_PIN_5;    //不支持或
    configStruct.mode = GPIO_MODE_OUT;
    configStruct.speed = GPIO_SPEED_50MHz;
    GPIO_Config(GPIOE, &configStruct);
	
    configStruct.pin = GPIO_PIN_6;    //不支持或
    GPIO_Config(GPIOE, &configStruct);
	
	  //设置GPIO为低电平,不点亮LED
    GPIO_SetBit(GPIOE, GPIO_PIN_5);
    GPIO_SetBit(GPIOE, GPIO_PIN_6);
}



void  KEY_GPIO_Config(void)
{
    GPIO_Config_T  configStruct;

    /** 开启GPIO_LED时钟 */
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOC);

    /** 配置GPIO_LED引脚 */
    GPIO_ConfigStructInit(&configStruct);
    configStruct.pin = GPIO_PIN_10;    //不支持或
    configStruct.mode = GPIO_MODE_IN;
    configStruct.speed = GPIO_SPEED_50MHz;
    GPIO_Config(GPIOC, &configStruct);
	
    configStruct.pin = GPIO_PIN_11;    //不支持或
    GPIO_Config(GPIOC, &configStruct);
}


void UART_init_Config(void)
{
	    /* USART configuration */
    USART_ConfigStructInit(&usartConfigStruct);
    usartConfigStruct.baudRate = 115200;
    usartConfigStruct.mode = USART_MODE_TX_RX;
    usartConfigStruct.parity = USART_PARITY_NONE;
    usartConfigStruct.stopBits = USART_STOP_BIT_1;
    usartConfigStruct.wordLength = USART_WORD_LEN_8B;
    usartConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;

    /* COM1 init*/
    APM_MINI_COMInit(COM1, &usartConfigStruct);
}


/*!
 * @brief     Main program
 *
 * @param     None
 *
 * @retval    None
 */
void Delay(void)
{
    volatile uint32_t delay = 0x2FFFFF;

    while (delay--);
}

/*!
 * @brief     USART write
 *
 * @param     dat:    data
 *
 * @param     len:    Data length
 *
 * @retval    None
 */
void USART_Write(uint8_t* dat, uint8_t len)
{
    uint8_t i;

    for (i = 0; i < len; i++)
    {
        while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);

        USART_TxData(USART1, dat[i]);
    }
}

//void KEY_Input(void)
//{
//	   LED2(KEY1);
//     LED3(KEY2);
//}


unsigned char key_read(void)
{
	static unsigned char key_up  = 1;
	if(key_up &&( (KEY1 == BIT_RESET) || (KEY2 == BIT_RESET) ) )
	{
     	SysTick_Delay_ms(10);
		  key_up = 0; 
		  if(KEY1 == BIT_RESET)   return  1;
		  else if(KEY2 == BIT_RESET)  return  2;
	
	}
	if(KEY1&&KEY2) key_up  = 1;
	return 0;
}


/*!
 * @brief     Main program
 *
 * @param     None
 *
 * @retval    None
 */
int main(void)
{
	  unsigned char key = 0;
	  
	  LED_GPIO_Config();
	  KEY_GPIO_Config();
	  SysTick_Init();
	  UART_init_Config();
    SHT3X_Init();
	
	 
	  printf("MCU init OK!\r\n");
	
   while (1)
   {
		 key = key_read();
		 switch (key)
		 {
			 case  1 :
				    LED2(0);
			      SysTick_Delay_ms(100);
			      SHT3X_TEST();  
			      LED2(1);
			     break;
				 
			 case  2 :
				    LED3(0);
			      SysTick_Delay_ms(100);
			      LED3(1);
			     break;
//        KEY_Input();
			 
     }
	 }
}


/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @param       *f:  pointer to a FILE that can recording all information
*              needed to control a stream
*
* @retval      The characters that need to be send.
*
* @note
*/
int fputc(int ch, FILE* f)
{
    /* send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, (uint8_t)ch);

    /* wait for the data to be send */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);

    return (ch);
}

下载验证     

按一下按键,就会打印下数据

image.png  

 

本帖最后由 尹小舟 于 2023-7-7 11:43 编辑

回复评论 (1)

sht30最高支持1000k的传输速率。因此通讯时间非常短。

 

为什么通讯时间非常短?

点赞  2023-7-8 08:56
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复