在学习M0G3507时,1. I2C协议 | 立创开发板技术文档中心,立创教程中使用的是SHT20,但是手上只有SHT30 这款,下面是学习过程遇到的问题

上面链接对于IIC的介绍等已经很全面了,但是我是第一次使用IIC 认为只要把源代码中SHT20的地址,读写命令按照SHT30的改就行了,但是这么粗略的改完之后的现象是读不到数据,且串口发送只能显示我要显示的printf语句的首字符,大概判断了一下应该是滴答定时器delay那部分的问题,没有深究,把代码换成源代码然后连接SHT30的现象是串口发送返回值 -1和-2,至此我也真正明白串口调试的含义了,在代码中标注对应的返回值是很有利于检查出错误的。

然后我找到了立创的另一款开发板移植SHT30的教程SHT30温湿度传感器 | 立创开发板技术文档中心

照着这个移植到M0芯片后编译无错误,但是无法下载,显示

这是原因

解决办法在这

接下来就是代码部分(前提已经配置好资源分配了)

bsp_sht30.c

//bsp_sht30.c
#include "bsp_sht30.h"
#include "stdio.h"
#include "board.h"

double Temperature = 0.0, Humidity = 0.0;

/******************************************************************
 * 函 数 名 称:IIC_Start
 * 函 数 说 明:IIC起始信号
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Start(void)
{
        SDA_OUT();
        SCL(1);
        SDA(0);

        SDA(1);
        delay_us(5);
        SDA(0);
        delay_us(5);

        SCL(0);
}

/******************************************************************
 * 函 数 名 称:IIC_Stop
 * 函 数 说 明:IIC停止信号
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Stop(void)
{
    SDA_OUT();
    SCL(0);
    SDA(0);
    SCL(1);
    delay_us(5);
    SDA(1);
    delay_us(5);
}

/******************************************************************
 * 函 数 名 称:IIC_Send_Ack
 * 函 数 说 明:主机发送应答或者非应答信号
 * 函 数 形 参:0发送应答  1发送非应答
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Send_Ack(unsigned char ack)
{
        SDA_OUT();
        SCL(0);
        SDA(0);
        delay_us(5);
        if(!ack) SDA(0);
        else     SDA(1);
        SCL(1);
        delay_us(5);
        SCL(0);
        SDA(1);
}


/******************************************************************
 * 函 数 名 称:IIC_Wait_Ack
 * 函 数 说 明:等待从机应答
 * 函 数 形 参:无
 * 函 数 返 回:1=无应答   0=有应答
 * 作       者:LC
 * 备       注:无
******************************************************************/
unsigned char I2C_WaitAck(void)
{

        char ack = 0;
        unsigned char ack_flag = 10;
        SCL(0);
        SDA(1);
        SDA_IN();

        SCL(1);
        while( (SDA_GET()==1) && ( ack_flag ) )
        {
                ack_flag--;
                delay_us(5);
        }

        if( ack_flag <= 0 )
        {
                IIC_Stop();
                return 1;
        }
        else
        {
                SCL(0);
                SDA_OUT();
        }
        return ack;
}

/******************************************************************
 * 函 数 名 称:Send_Byte
 * 函 数 说 明:写入一个字节
 * 函 数 形 参:dat要写入的数据
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void Send_Byte(u8 dat)
{
        int i = 0;
        SDA_OUT();
        SCL(0);//拉低时钟开始数据传输

        for( i = 0; i < 8; i++ )
        {
                SDA( (dat & 0x80) >> 7 );
                delay_us(1);
                SCL(1);
                delay_us(5);
                SCL(0);
                delay_us(5);
                dat<<=1;		//dat 都会左移,确保下一次迭代可以发送下一个最高位
        }
}

/******************************************************************
 * 函 数 名 称:Read_Byte
 * 函 数 说 明:IIC读时序
 * 函 数 形 参:无
 * 函 数 返 回:读到的数据
 * 作       者:LC
 * 备       注:无
******************************************************************/
unsigned char Read_Byte(void)
{
    unsigned char i,receive=0;
    SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
    {
        SCL(0);
        delay_us(5);
        SCL(1);
        delay_us(5);
        receive<<=1;
        if( SDA_GET() )
        {
            receive|=1;
        }
        delay_us(5);
    }
        SCL(0);
    return receive;
}
/******************************************************************
 * 函 数 名 称:SHT31_Write_mode
 * 函 数 说 明:在周期模式下设置测量周期与可重复性命令
 * 函 数 形 参:dat设置命令
                常用的有:每一秒采集0.5次  0x2024
                          每一秒采集1次    0x2126
                          每一秒采集2次    0x2220
                          每一秒采集4次    0x2334
                          每一秒采集10次   0x2721
 * 函 数 返 回:
 * 作       者:LC
 * 备       注:
******************************************************************/
char SHT31_Write_mode(uint16_t dat)
{
        IIC_Start();

        // << 1 是将最后一位置0,设置为写命令
        Send_Byte((0X44 << 1) | 0 );
        //返回0为产生了应答,返回1说明通信失败
        if( I2C_WaitAck() == 1 )
		{
			printf("error -1\r\n");
			return 1;
		}
        //发送命令的高8位
        Send_Byte((dat >> 8 ) );
        //返回0为产生了应答,返回1说明通信失败
        if( I2C_WaitAck() == 1 )
		{
			printf("error -2\r\n");
			return 2;
		}
        //发送命令的低8位
        Send_Byte(dat & 0xff );
        //返回0为产生了应答,返回1说明通信失败
        if( I2C_WaitAck() == 1 )
		{
			printf("error -3\r\n");
			return 3;
		}
//        IIC_Stop();
        return 0;
}

/******************************************************************
 * 函 数 名 称:crc8
 * 函 数 说 明:CRC校验
 * 函 数 形 参:data要校验的数据地址     len要校验的长度
 * 函 数 返 回:校验后的值
 * 作       者:LC
 * 备       注:无
******************************************************************/
unsigned char crc8(const unsigned char *data, int len)
{
        const unsigned char POLYNOMIAL = 0x31;
        unsigned char crc = 0xFF;
        int j, i;

        for (j=0; j<len; j++)
        {
                crc ^= *data++;
                for ( i = 0; i <8; i++ )
                {
                        crc = ( crc & 0x80 ) ? (crc << 1) ^ POLYNOMIAL : (crc << 1);
                }
        }
        return crc;
}

/******************************************************************
 * 函 数 名 称:SHT30_Read
 * 函 数 说 明:读取温湿度值
 * 函 数 形 参:dat读取的命令
                周期模式命令为:0xe000
                单次模式命令为:0x2c06 or 0x2400
 * 函 数 返 回:0读取成功  其他失败
 * 作       者:LC
 * 备       注:当前为周期模式读取,如使用单次模式,则将
 *              【设置周期模式命令】下的命令注释即可。
******************************************************************/
char SHT30_Read(uint16_t dat)
{
        uint16_t i = 0;
        unsigned char buff[6] = {0};// 初始化一个6字节的缓冲区,用于存储从SHT30读取的数据
        uint16_t data_16 = 0;		// 初始化一个16位的数据变量,用于暂存处理的数据

        //设置周期模式命令
        SHT31_Write_mode(0x2130);//每1秒一次高重复测量(需要在周期模式下才有用)

        IIC_Start();
        Send_Byte( (0x44<<1) | 0);		// 发送设备地址和写操作命令,0x44是SHT30的I2C地址,左移一位后最低位为0表示写操作
        if( I2C_WaitAck() == 1 )return 1;// 等待设备应答,如果应答失败则返回1
        Send_Byte((dat >> 8 ));			// 发送命令的高8位
        if( I2C_WaitAck() == 1 )return 2;// 等待设备应答,如果应答失败则返回2
        Send_Byte( dat & 0xff );		// 发送命令的低8位
        if( I2C_WaitAck() == 1 )return 3;// 等待设备应答,如果应答失败则返回3

        //如不使用超时判断,很容易数据错乱
        do
        {
                //超时判断
                i++;
                if( i > 20 ) return 4;
                delay_ms(2);

                IIC_Start();
                Send_Byte((0X44 << 1) | 1 );//读

        }while(I2C_WaitAck() == 1);        //读取到温湿度数据则结束读命令

        //温度高8位
        buff[0] = Read_Byte();
        IIC_Send_Ack(0);
        //温度低8位
        buff[1] = Read_Byte();
        IIC_Send_Ack(0);
        //温度CRC校验值
        buff[2] = Read_Byte();
        IIC_Send_Ack(0);
        //湿度高8位
        buff[3] = Read_Byte();
        IIC_Send_Ack(0);
        //湿度低8位
        buff[4] = Read_Byte();
        IIC_Send_Ack(0);
        //湿度CRC校验值
        buff[5] = Read_Byte();
        IIC_Send_Ack(1);

        IIC_Stop();

        //CRC校验(将要校验的数值带入,查看计算后的校验值是否和读取到的校验值一致)
        if( (crc8(buff,2) == buff[2]) && ( crc8(buff+3,2) == buff[5]) )
        {
                //计算温度值
                data_16 =(buff[0]<<8) | buff[1];
                Temperature = (data_16/65535.0)*175.0 - 45;
                //计算湿度值
                data_16 = 0;
                data_16 =(buff[3]<<8) | buff[4];
                Humidity = (data_16/65535.0) * 100.0;

                return 0;
        }
        else
        {
                printf("Wrong\r\n");
        }
        return 5;
}

bsp_sht30.h

//bsp_sht30.h
#ifndef __BSP_SHT30_H__
#define __BSP_SHT30_H__

#include "board.h"
#include "stdio.h"

extern double Temperature, Humidity;

#define u8 unsigned char


//设置SDA输出模式
#define SDA_OUT()   {                                                \
                        DL_GPIO_initDigitalOutput(I2C_SDA_IOMUX);    \
                        DL_GPIO_setPins(I2C_PORT, I2C_SDA_PIN);      \
                        DL_GPIO_enableOutput(I2C_PORT, I2C_SDA_PIN); \
                    }
//设置SDA输入模式
#define SDA_IN()    { DL_GPIO_initDigitalInput(I2C_SDA_IOMUX); }
//获取SDA引脚的电平变化
#define SDA_GET()   ( ( ( DL_GPIO_readPins(I2C_PORT,I2C_SDA_PIN) & I2C_SDA_PIN ) > 0 ) ? 1 : 0 )
//SDA与SCL输出
#define SDA(x)      ( (x) ? (DL_GPIO_setPins(I2C_PORT,I2C_SDA_PIN)) : (DL_GPIO_clearPins(I2C_PORT,I2C_SDA_PIN)) )
#define SCL(x)      ( (x) ? (DL_GPIO_setPins(I2C_PORT,I2C_SCL_PIN)) : (DL_GPIO_clearPins(I2C_PORT,I2C_SCL_PIN)) )


char SHT30_Read(uint16_t dat);

#endif

board.c

#include "board.h"
#include "stdio.h"

#define RE_0_BUFF_LEN_MAX	128

volatile uint8_t  recv0_buff[RE_0_BUFF_LEN_MAX] = {0};
volatile uint16_t recv0_length = 0;
volatile uint8_t  recv0_flag = 0;

void board_init(void)
{
	// SYSCFG初始化
	SYSCFG_DL_init();
	//清除串口中断标志
	NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
	//使能串口中断
	NVIC_EnableIRQ(UART_0_INST_INT_IRQN);
	
	printf("Board Init [[ ** LCKFB ** ]]\r\n");
}

//搭配滴答定时器实现的精确us延时
void delay_us(unsigned long __us) 
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 38;

    // 计算需要的时钟数 = 延迟微秒数 * 每微秒的时钟数
    ticks = __us * (32000000 / 1000000);

    // 获取当前的SysTick值
    told = SysTick->VAL;

    while (1)
    {
        // 重复刷新获取当前的SysTick值
        tnow = SysTick->VAL;

        if (tnow != told)
        {
            if (tnow < told)
                tcnt += told - tnow;
            else
                tcnt += SysTick->LOAD - tnow + told;

            told = tnow;

            // 如果达到了需要的时钟数,就退出循环
            if (tcnt >= ticks)
                break;
        }
    }
}
//搭配滴答定时器实现的精确ms延时
void delay_ms(unsigned long ms) 
{
	delay_us( ms * 1000 );
}

void delay_1us(unsigned long __us){ delay_us(__us); }
void delay_1ms(unsigned long ms){ delay_ms(ms); }

//串口发送单个字符
void uart0_send_char(char ch)
{
	//当串口0忙的时候等待,不忙的时候再发送传进来的字符
	while( DL_UART_isBusy(UART_0_INST) == true );
	//发送单个字符
	DL_UART_Main_transmitData(UART_0_INST, ch);

}
//串口发送字符串
void uart0_send_string(char* str)
{
	//当前字符串地址不在结尾 并且 字符串首地址不为空
	while(*str!=0&&str!=0)
	{
		//发送字符串首地址中的字符,并且在发送完成之后首地址自增
		uart0_send_char(*str++);
	}
}


#if !defined(__MICROLIB)
//不使用微库的话就需要添加下面的函数
#if (__ARMCLIB_VERSION <= 6000000)
//如果编译器是AC5  就定义下面这个结构体
struct __FILE
{
	int handle;
};
#endif

FILE __stdout;

//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
	x = x;
}
#endif


//printf函数重定义
int fputc(int ch, FILE *stream)
{
	//当串口0忙的时候等待,不忙的时候再发送传进来的字符
	while( DL_UART_isBusy(UART_0_INST) == true );
	
	DL_UART_Main_transmitData(UART_0_INST, ch);
	
	return ch;
}

//串口的中断服务函数
void UART_0_INST_IRQHandler(void)
{
	uint8_t receivedData = 0;
	
	//如果产生了串口中断
	switch( DL_UART_getPendingInterrupt(UART_0_INST) )
	{
		case DL_UART_IIDX_RX://如果是接收中断
			
			// 接收发送过来的数据保存
			receivedData = DL_UART_Main_receiveData(UART_0_INST);

			// 检查缓冲区是否已满
			if (recv0_length < RE_0_BUFF_LEN_MAX - 1)
			{
				recv0_buff[recv0_length++] = receivedData;

				// 将保存的数据再发送出去,不想回传可以注释掉
				uart0_send_char(receivedData);
			}
			else
			{
				recv0_length = 0;
			}

			// 标记接收标志
			recv0_flag = 1;
		
			break;
		
		default://其他的串口中断
			break;
	}
}

board.h

#ifndef	__BOARD_H__
#define __BOARD_H__

#include "ti_msp_dl_config.h"

void board_init(void);

void delay_us(unsigned long __us);
void delay_ms(unsigned long ms);
void delay_1us(unsigned long __us);
void delay_1ms(unsigned long ms);

void uart0_send_char(char ch);
void uart0_send_string(char* str);



#endif

empty.c(主函数)

#include "board.h"
#include <stdio.h>
#include "bsp_sht30.h"

int main(void)
{
    //开发板初始化
    board_init();
        
    printf("SHT30 Start!!\r\n");
        
    while(1) 
    {
		SHT30_Read(0xe000);

        printf("Temp = %.2f\r\n",Temperature);
        printf("Humi = %.2f\r\n",Humidity);
        printf("\r\n");
        delay_ms(1000);
    }
}

感谢立创的文档教程。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐