OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4062|回复: 9

IIC读取问题,命令能写进去,但是读数据不对。

[复制链接]

4

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
59
金钱
59
注册时间
2020-10-16
在线时间
30 小时
发表于 2020-11-24 17:29:28 | 显示全部楼层 |阅读模式
20金钱
本帖最后由 zly0516 于 2020-11-25 10:21 编辑

模拟例程,读取max30102的六组数据时,第一组没读到,后五组读到是0xff,代码如下,求各位大神帮帮忙,问题出在哪里?

#include "myiic.h"
#include "delay.h"

//初始化IIC
void IIC_Init(void)
{
  //IO口配置
        //PA9---> SCL,PA10 ---> SDA

GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */

  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = IIC_SCL_PIN | IIC_SDA_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIO_PORT_IIC, &GPIO_InitStruct);

   IIC_SCL_1;
   IIC_SDA_1;

}
//产生IIC起始信号
void IIC_Start(void)
{
    SDA_OUT();      //SDA线输出
   IIC_SDA_1;
   IIC_SCL_1;
   delay_us(4);
    IIC_SDA_0; //START:when CLK is high,DATA change form high to low
   delay_us(4);
   IIC_SCL_0 ; //钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
    SDA_OUT();//sda线输出
    IIC_SCL_0;
    IIC_SDA_0; //STOP:when CLK is high DATA change form low to high
    delay_us(4);
    IIC_SCL_1;
    IIC_SDA_1; //发送I2C总线结束信号
    delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{
    uint8_t ucErrTime = 0;
//             SDA_read;
    SDA_IN();      //SDA设置为输入
     IIC_SDA_1;
    delay_us(1);
     IIC_SCL_1;
    delay_us(1);
    while(READ_SDA)
    {
        ucErrTime++;
        if(ucErrTime > 250)
        {
            IIC_Stop();
            return 1;
        }
    }
    IIC_SCL_0; //时钟输出0
    return 0;
}

//产生ACK应答
void IIC_Ack(void)
{
    IIC_SCL_0;
    SDA_OUT();
    IIC_SDA_0;
    delay_us(2);
    IIC_SCL_1;
    delay_us(2);
    IIC_SCL_1;
}
//不产生ACK应答
void IIC_NAck(void)
{
    IIC_SCL_0;
    SDA_OUT();
    IIC_SDA_1;
    delay_us(2);
    IIC_SCL_1;
    delay_us(2);
    IIC_SCL_0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(uint8_t txd)
{
    uint8_t t;
    SDA_OUT();
    IIC_SCL_0; //拉低时钟开始数据传输
    for(t = 0; t < 8; t++)
    { if((txd & 0x80) >> 7) IIC_SDA_1;
      else IIC_SDA_0;
//        IIC_SDA = (txd & 0x80) >> 7;
        txd <<= 1;
        delay_us(2);   //对TEA5767这三个延时都是必须的
        IIC_SCL_1;
        delay_us(2);
        IIC_SCL_0;
        delay_us(2);
    }
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uint8_t IIC_Read_Byte(unsigned char ack)
{
    unsigned char i, receive = 0;

    SDA_IN();//SDA设置为输入
    for(i = 0; i < 8; i++ )
    {
        IIC_SCL_0;
        delay_us(2);
        IIC_SCL_1;
        receive <<= 1;
        if(READ_SDA)receive++;
        delay_us(1);
    }
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK
    return receive;
}


void IIC_WriteBytes(uint8_t WriteAddr, uint8_t* data, uint8_t dataLength)
{
    uint8_t i;
    IIC_Start();

    IIC_Send_Byte(WriteAddr);            //发送写命令
    IIC_Wait_Ack();

    for(i = 0; i < dataLength; i++)
    {
        IIC_Send_Byte(data);
        IIC_Wait_Ack();
    }
    IIC_Stop();//产生一个停止条件
//    delay_ms(10);
                HAL_Delay(10);
}

void IIC_ReadBytes(uint8_t deviceAddr, uint8_t writeAddr, uint8_t* data, uint8_t dataLength)
{
    uint8_t i;
    IIC_Start();

    IIC_Send_Byte(deviceAddr);            //发送写命令
    IIC_Wait_Ack();
    IIC_Send_Byte(writeAddr);
    IIC_Wait_Ack();
    IIC_Send_Byte(deviceAddr | 0X01); //进入接收模式
    IIC_Wait_Ack();

    for(i = 0; i < dataLength - 1; i++)
    {
        data = IIC_Read_Byte(1);
    }
    data[dataLength - 1] = IIC_Read_Byte(0);
    IIC_Stop();//产生一个停止条件
//    delay_ms(10);
                HAL_Delay(10);
}




#ifndef __MYIIC_H
#define __MYIIC_H

#include "main.h"
#include "stm32l0xx_hal.h"
#include "stdint.h"                              

//#define SDA_IN()  {GPIOA->MODER&=0XFFCFFFFF;GPIOA->PUPDR&=0XFFCFFFFF;GPIOA->PUPDR|=(uint32_t)1<<19;}
//#define SDA_OUT() {GPIOA->MODER&=0XFFCFFFFF;GPIOA->MODER|=(uint32_t)1<<19;GPIOA->OSPEEDR&=0XFFCFFFFF;GPIOA->OSPEEDR|=(uint32_t)2<<19;}

#define SDA_IN()  {GPIO_InitTypeDef GPIO_InitStruct = {0};\
                   __HAL_RCC_GPIOA_CLK_ENABLE();\
                   GPIO_InitStruct.Pin = IIC_SDA_PIN;\
                   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;\
                   GPIO_InitStruct.Pull = GPIO_PULLUP;\
                   HAL_GPIO_Init(GPIO_PORT_IIC, &GPIO_InitStruct);}

#define SDA_OUT() {GPIO_InitTypeDef GPIO_InitStruct = {0};\
                   __HAL_RCC_GPIOA_CLK_ENABLE();\
                   GPIO_InitStruct.Pin = IIC_SDA_PIN;\
                   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;\
                   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;\
                   HAL_GPIO_Init(GPIO_PORT_IIC, &GPIO_InitStruct);}

//IO操作函数        


#define GPIO_PORT_IIC        GPIOA                              /* GPIO端口 */
#define IIC_SCL_PIN                GPIO_PIN_9                        /* 连接到SCL时钟线的GPIO */
#define IIC_SDA_PIN                GPIO_PIN_10                        /* 连接到SDA数据线的GPIO */

#define IIC_SCL_1  HAL_GPIO_WritePin(GPIO_PORT_IIC, IIC_SCL_PIN,GPIO_PIN_SET)                /* SCL = 1 */
#define IIC_SCL_0  HAL_GPIO_WritePin(GPIO_PORT_IIC, IIC_SCL_PIN,GPIO_PIN_RESET)                /* SCL = 0 */
        
#define IIC_SDA_1  HAL_GPIO_WritePin(GPIO_PORT_IIC, IIC_SDA_PIN,GPIO_PIN_SET)                /* SDA = 1 */
#define IIC_SDA_0  HAL_GPIO_WritePin(GPIO_PORT_IIC, IIC_SDA_PIN,GPIO_PIN_RESET)                /* SDA = 0 */
        
#define READ_SDA   HAL_GPIO_ReadPin(GPIO_PORT_IIC , IIC_SDA_PIN)

                                                
                                                                        
                                                                        
//#define IIC_SCL    PBout(9) //SCL
//#define IIC_SDA    PBout(10) //SDA         
//#define READ_SDA   PBin(10)  //输入SDA

//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口                                 
void IIC_Start(void);                                //发送IIC开始信号
void IIC_Stop(void);                                  //发送IIC停止信号
void IIC_Send_Byte(uint8_t txd);                        //IIC发送一个字节
uint8_t IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
uint8_t IIC_Wait_Ack(void);                                 //IIC等待ACK信号
void IIC_Ack(void);                                        //IIC发送ACK信号
void IIC_NAck(void);                                //IIC不发送ACK信号
void IIC_WriteBytes(uint8_t WriteAddr,uint8_t* data,uint8_t dataLength);
void IIC_ReadBytes(uint8_t deviceAddr, uint8_t writeAddr,uint8_t* data,uint8_t dataLength);

#include "max30102.h"
#include "myiic.h"
#include "delay.h"

u8 max30102_Bus_Write(u8 Register_Address, u8 Word_Data)
{
        /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
        /* 第1步:发起I2C总线启动信号 */
        IIC_Start();
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        IIC_Send_Byte(max30102_WR_address | I2C_WR);        /* 此处是写指令 */
        /* 第3步:发送ACK */
        if (IIC_Wait_Ack() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }
        /* 第4步:发送字节地址 */
        IIC_Send_Byte(Register_Address);
        if (IIC_Wait_Ack() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }        
        /* 第5步:开始写入数据 */
        IIC_Send_Byte(Word_Data);
        /* 第6步:发送ACK */
        if (IIC_Wait_Ack() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }
        /* 发送I2C总线停止信号 */
        IIC_Stop();
        return 1;        /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
        /* 发送I2C总线停止信号 */
        IIC_Stop();
        return 0;
}

u8 max30102_Bus_Read(u8 Register_Address)
{
        u8  data;
        /* 第1步:发起I2C总线启动信号 */
        IIC_Start();
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        IIC_Send_Byte(max30102_WR_address | I2C_WR);        /* 此处是写指令 */
        /* 第3步:发送ACK */
        if (IIC_Wait_Ack() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }
        /* 第4步:发送字节地址, */
        IIC_Send_Byte((uint8_t)Register_Address);
        if (IIC_Wait_Ack() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }
        /* 第6步:重新启动I2C总线。下面开始读取数据 */
        IIC_Start();
        /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        IIC_Send_Byte(max30102_WR_address | I2C_RD);        /* 此处是读指令 */
        /* 第8步:发送ACK */
        if (IIC_Wait_Ack() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }
        /* 第9步:读取数据 */
        {
                data = IIC_Read_Byte(0);        /* 读1个字节 */
                IIC_NAck();        /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
        }
        /* 发送I2C总线停止信号 */
        IIC_Stop();
        return data;        /* 执行成功 返回data值 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
        /* 发送I2C总线停止信号 */
        IIC_Stop();
        return 0;
}

void max30102_FIFO_ReadBytes(u8 Register_Address,u8* Data)
{        
        max30102_Bus_Read(REG_INTR_STATUS_1);
        max30102_Bus_Read(REG_INTR_STATUS_2);        
        /* 第1步:发起I2C总线启动信号 */
        IIC_Start();
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        IIC_Send_Byte(max30102_WR_address | I2C_WR);        /* 此处是写指令 */
        /* 第3步:发送ACK */
        if (IIC_Wait_Ack() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }
        /* 第4步:发送字节地址, */
        IIC_Send_Byte((uint8_t)Register_Address);
        if (IIC_Wait_Ack() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }
        /* 第6步:重新启动I2C总线。下面开始读取数据 */
        IIC_Start();
        /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        IIC_Send_Byte(max30102_WR_address | I2C_RD);        /* 此处是读指令 */
        /* 第8步:发送ACK */
        if (IIC_Wait_Ack() != 0)
        {
                goto cmd_fail;        /* EEPROM器件无应答 */
        }
        /* 第9步:读取数据 */
        Data[0] = IIC_Read_Byte(1);        
        Data[1] = IIC_Read_Byte(1);        
        Data[2] = IIC_Read_Byte(1);        
        Data[3] = IIC_Read_Byte(1);
        Data[4] = IIC_Read_Byte(1);        
        Data[5] = IIC_Read_Byte(0);
        /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
        /* 发送I2C总线停止信号 */
        IIC_Stop();
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
        /* 发送I2C总线停止信号 */
        IIC_Stop();
}
//max30102初始化
void max30102_init(void)
{
        
        IIC_Init();
        max30102_reset();
        max30102_Bus_Write(REG_INTR_ENABLE_1,0xc0);        // INTR setting
        max30102_Bus_Write(REG_INTR_ENABLE_2,0x00);
        max30102_Bus_Write(REG_FIFO_WR_PTR,0x00);          //FIFO_WR_PTR[4:0]
        max30102_Bus_Write(REG_OVF_COUNTER,0x00);          //OVF_COUNTER[4:0]
        max30102_Bus_Write(REG_FIFO_RD_PTR,0x00);          //FIFO_RD_PTR[4:0]
        max30102_Bus_Write(REG_FIFO_CONFIG,0x0f);          //sample avg = 1, fifo rollover=false, fifo almost full = 17
        max30102_Bus_Write(REG_MODE_CONFIG,0x03);          //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
        max30102_Bus_Write(REG_SPO2_CONFIG,0x27);          // SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)  
        max30102_Bus_Write(REG_LED1_PA,0x24);           //Choose value for ~ 7mA for LED1
        max30102_Bus_Write(REG_LED2_PA,0x24);           // Choose value for ~ 7mA for LED2
        max30102_Bus_Write(REG_PILOT_PA,0x7f);           // Choose value for ~ 25mA for Pilot LED                                                                                
}
//max30102复位
void max30102_reset(void)
{
        max30102_Bus_Write(REG_MODE_CONFIG,0x40);
        max30102_Bus_Write(REG_MODE_CONFIG,0x40);
}
主函数的一部分:
for(i = 0; i < n_ir_buffer_length; i++)
    {     
        while(MAX30102_INT == 1); //wait until the interrupt pin asserts
        max30102_FIFO_ReadBytes(REG_FIFO_DATA, temp);
        aun_red_buffer =  (long)((long)((long)temp[0] & 0x03) << 16) | (long)temp[1] << 8 | (long)temp[2]; // Combine values to get the actual number
        aun_ir_buffer = (long)((long)((long)temp[3] & 0x03) << 16) | (long)temp[4] << 8 | (long)temp[5]; // Combine values to get the actual number

        if(un_min > aun_red_buffer)
            un_min = aun_red_buffer;  //update signal min
        if(un_max < aun_red_buffer)
            un_max = aun_red_buffer;  //update signal max
    }


  max30102_FIFO_ReadBytes(REG_FIFO_DATA, temp);通过这个函数读取数据,但是数据不对。


前面命令都对,就是读取数据时,第一个没读到,后面全是0xff,单片机用的是stm31l0系列的,用hal改写的。

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2020-11-25 01:10:46 | 显示全部楼层
测量下波形吧,你这个设备有反应没有先。
回复

使用道具 举报

4

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
59
金钱
59
注册时间
2020-10-16
在线时间
30 小时
 楼主| 发表于 2020-11-25 09:07:57 | 显示全部楼层
有返回响应的,写命令可以,读不行。
回复

使用道具 举报

34

主题

255

帖子

0

精华

高级会员

Rank: 4

积分
912
金钱
912
注册时间
2019-7-5
在线时间
189 小时
发表于 2020-11-25 09:09:14 | 显示全部楼层
首先通过逻辑分析仪或者示波器看看波形,判断一下你的I2C读写函数是否正确,然后查看从机的地址是否正确,一步步往下查
回复

使用道具 举报

0

主题

36

帖子

0

精华

高级会员

Rank: 4

积分
688
金钱
688
注册时间
2018-9-6
在线时间
249 小时
发表于 2020-11-25 09:39:44 | 显示全部楼层
你等待应答的时序错了
回复

使用道具 举报

4

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
59
金钱
59
注册时间
2020-10-16
在线时间
30 小时
 楼主| 发表于 2020-11-25 09:58:19 | 显示全部楼层
liaohaijian 发表于 2020-11-25 09:09
首先通过逻辑分析仪或者示波器看看波形,判断一下你的I2C读写函数是否正确,然后查看从机的地址是否正确, ...

器件地址改不了,应该错不了。
回复

使用道具 举报

4

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
59
金钱
59
注册时间
2020-10-16
在线时间
30 小时
 楼主| 发表于 2020-11-25 09:59:02 | 显示全部楼层
yun1003 发表于 2020-11-25 09:39
你等待应答的时序错了

怎么改,我该半天了。
回复

使用道具 举报

4

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
59
金钱
59
注册时间
2020-10-16
在线时间
30 小时
 楼主| 发表于 2020-11-25 10:03:44 | 显示全部楼层
zly0516 发表于 2020-11-25 09:58
器件地址改不了,应该错不了。

用逻辑分析仪看了写的命令和地址都是对的,就是在读的时候,不对。
回复

使用道具 举报

4

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
59
金钱
59
注册时间
2020-10-16
在线时间
30 小时
 楼主| 发表于 2020-11-25 10:09:30 | 显示全部楼层
本帖最后由 zly0516 于 2020-11-25 10:11 编辑
zly0516 发表于 2020-11-25 09:59
怎么改,我该半天了。

C:\Users\Administrator.SKY-20200424ZEX\Desktop
NM27CUZRO0(GAP_)PO3(F{Q.png
回复

使用道具 举报

34

主题

255

帖子

0

精华

高级会员

Rank: 4

积分
912
金钱
912
注册时间
2019-7-5
在线时间
189 小时
发表于 2020-11-25 18:58:15 | 显示全部楼层

你这时钟时序都错了,网上找一下软件I2C读写例程参照一下,网上有很多的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-22 21:10

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表