M0G3507 IIC通信SHT30温湿度传感器代码和遇到的问题
·
在学习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);
}
}
感谢立创的文档教程。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)