This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TM4C1233H6PM:通过 I2C 从 HMC6352读取标题数据时出现问题

Guru**** 2477885 points
Other Parts Discussed in Thread: TM4C1233H6PM

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/687004/tm4c1233h6pm-problem-reading-heading-data-from-hmc6352-via-i2c

器件型号:TM4C1233H6PM

您好!

我已经运行了 TIvaware 文件夹中的 I2C 环回示例、并从中尝试设置通信
一个相当旧的罗盘传感器部件 HMC6352。

它可以与 Arduino sketch 配合使用、但无法与 TIvaware 配合使用。 这是我的代码。 我是否错过了或错误了什么?

此器件的从器件地址为0x42表示写入、0x43表示读取。 我认为这是对设置从地址函数中的 true 和 false 条件的处理。

该器件应在100kbps 模式下运行、但不确定在 driverlib I2C 中为什么设置了默认值。

//
// HMC6352.c
//*********

#include 
#include 
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctio.uarth"
#include "UART"




//
//
//要接收的 I2C 数据包数量。
////
*****************
#define NUM_I2C_DATA 2

//*********

//
//设置从机模块的地址。 这是以
//以下格式发送的7位地址:
// [A6:A5:A4:A3:A2:A1:A0:RS]
//
第一个字节的"RS"位置为零表示主
设备//向所选的从设备发送(发送)数据,而在该位置为1
表示主设备从设备接收数据。
////
*****************
#define SLAVE_ADDRESS 0x42

//*********
//
//此函数将 UART0设置为用于控制台,以便
在示例运行时显示信息//。
////
*****************
void
InitConsole (void)
{
//
//启用用于 UART0引脚的 GPIO 端口 A。
// TODO:将其更改为您正在使用的 GPIO 端口。
//
SysCtlPeripheralEnable (SYSCTL_Periph_GPIOA);

//
//为端口 A0和 A1上的 UART0功能配置引脚复用。
//如果您的器件不支持引脚复用、则无需执行此步骤。
// TODO:更改此选项以选择您正在使用的端口/引脚。
//
GPIOPinConfigure (GPIO_PA0_U0RX);
GPIOPinConfigure (GPIO_PA1_U0TX);

//
//启用 UART0以便我们可以配置时钟。
//
SysCtlPeripheralEnable (SYSCTL_Periph_UART0);

//
//使用内部16MHz 振荡器作为 UART 时钟源。
//
UARTClockSourceSet (UART0_BASE、UART_CLOCK_PIOSC);

//
//为这些引脚选择替代(UART)功能。
// TODO:更改此选项以选择您正在使用的端口/引脚。
//
GPIOPinTypeUART (GPIO_Porta_base、GPIO_PIN_0 | GPIO_PIN_1);

//
//初始化控制台 I/O 的 UART
//
UARTStdioConfig (0、115200、16000000);
}

//*********
//
//配置 I2C0主机。
////
*****************
int
main (void)
{
#if defined (target_IS_TM4C129_RA0)|| \
已定义(TARGET_IS_TM4C129_RA1)|| \
已定义(TARGET_IS_TM4C129_RA2)
uint32_t ui32SysClock;
#endif
uint32_t pui32CmdTx;
uint32_t pui32DataRx[NUM_I2C_DATA];
uint32_t ui32Index;

//
//将时钟设置为直接从外部晶振/振荡器运行。
// TODO:必须更改 SYSCTL_XTAL_VALUE 以匹配的值
板上的//晶体。
//
#if defined (target_IS_TM4C129_RA0)|| \
已定义(TARGET_IS_TM4C129_RA1)|| \
已定义(TARGET_IS_TM4C129_RA2)
ui32SysClock = SysCtlClockFreqSet ((SYSCTL_XTAL_25MHz |
SYSCTL_OSC_MAIN |
SYSCTL_USE_OSC)、25000000);
#else
SysCtlClockSet (SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHz);
#endif

//
//使用前必须启用 I2C0外设。
//
SysCtlPeripheralEnable (SYSCTL_Periph_I2C0);

//
//对于本示例,I2C0与 PortB[3:2]一起使用。 实际端口和
//使用的引脚可能与您的器件不同、请参阅的数据表
//更多信息。 GPIO 端口 B 需要启用、因此这些引脚可以
//使用。
// TODO:将其更改为您正在使用的 GPIO 端口。
//
SysCtlPeripheralEnable (SYSCTL_Periph_GPIOB);

//
//为端口 B2和 B3上的 I2C0功能配置引脚复用。
//如果您的器件不支持引脚复用、则无需执行此步骤。
// TODO:更改此选项以选择您正在使用的端口/引脚。
//
GPIOPinConfigure (GPIO_PB2_I2C0SCL);
GPIOPinConfigure (GPIO_PB3_I2C0SDA);

//
//为这些引脚选择 I2C 功能。 此函数也会
//为 I2C 操作配置 GPIO 引脚,将其设置为
//开漏操作,弱上拉。 请参阅数据表
//查看每个引脚分配了哪些功能。
// TODO:更改此选项以选择您正在使用的端口/引脚。
//
GPIOPinTypeI2CSCL (GPIO_PORTB_BASE、GPIO_PIN_2);
GPIOPinTypeI2C (GPIO_PORTB_BASE、GPIO_PIN_3);

//
//启用和初始化 I2C0主机模块。 使用的系统时钟
// I2C0模块。 最后一个参数设置 I2C 数据传输速率。
//如果为 false,则数据速率设置为100kbps,如果为 true,则数据速率将设置为
//设置为400kbps。 在本示例中、我们将使用100kbps 的数据速率。
//
#if defined (target_IS_TM4C129_RA0)|| \
已定义(TARGET_IS_TM4C129_RA1)|| \
已定义(TARGET_IS_TM4C129_RA2)
I2CMasterInitExpClk (I2C0_BASE、ui32SysClock、false);
#else
I2CMasterInitExpClk (I2C0_BASE、SysCtlClockGet ()、false);
#endif

//
//告诉主模块何时将在总线上放置什么地址
//与从设备通信。 将地址设置为 SLAVE_ADDRESS
//(在从机模块中设置)。 接收参数设置为 false
//表示 I2C 主设备正在向从设备发起写入操作。 如果
// true、这表示 I2C 主设备正在启动读取
//从器件。
//
I2CMasterSlaveAddrSet (I2C0_BASE、SLAVE_ADDRESS、FALSE);

//
//设置用于显示消息的串行控制台。 这是
//仅用于此示例程序,I2C 操作不需要。
//
InitConsole();

//
//在控制台上显示示例设置。
//
UARTprintf ("标题->");

pui32CmdTx ='A';

//
//将要发送的数据放在数据寄存器中
//
I2CMasterDataPut (I2C0_BASE、pui32CmdTx);

//
//开始从主器件发送数据。
//
I2CMasterControl (I2C0_BASE、I2C_MASTER_CMD_SINGLE_SEND);

//
//等待从机接收并确认数据。
//
while (!(I2CSlaveStatus (I2C0_BASE)& I2C_SLAVE_ACT_RREQ))
{
}

//
//将数据方向修改为 true,以便查看地址
//表示 I2C 主设备正在从设备发起读取。
//
I2CMasterSlaveAddrSet (I2C0_BASE、SLAVE_ADDRESS、TRUE);

for (ui32Index = 0;ui32Index < NUM_I2C_DATA; ui32Index++)
{
//
//指示主器件读取数据。
//
I2CMasterControl (I2C0_BASE、I2C_MASTER_CMD_SINGLE_Receive);

//
//等待从机完成发送数据。
//
while (!(I2CSlaveStatus (I2C0_BASE)& I2C_SLAVE_ACT_TREQ))
{
}

//
//从主设备读取数据。
//
pui32DataRx[ui32Index]= I2CMasterDataGet (I2C0_BASE);
//
//显示从机已接收到的数据。
//

UARTprintf ("'%c"、pui32DataRx[ui32Index]);
}

//UARTprintf ("\n");

}

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    需要查看的代码很多。   通过快速扫描-没有任何主要的跳转。

    种正常/习惯 I2C 调试策略包括:

    • 在每个 I2C 线路上安装外部上拉电阻  器(MCU 的内部值"过高"-不是特别可靠)
    • 监控两条具有范围的 I2C 线路-以确保数据的存在-然后数据的适当性

    串行接口-由于其"连接易用"-受到高度青睐-但(通常)需要"信号线监控"-以确保正确性...

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Michael、

     请参阅以下源代码以设置从地址。 您可以看到、在写入 I2CMSA 寄存器之前、7位从地址首先左移一位。 您的从器件地址不应为0x42、因为这是一个8位值、包含从器件地址和 R/W 位。  您应该给出7位地址作为#define SLAVE_ADDRESS 0x21。  

     来自 CB1的其他建议、例如 SDA 和 SCL 总线上的适当上拉电阻器也很重要、需要检查。   

    //
    //
    //! 设置 I2C 主设备放置在总线上的地址。
    //!
    //! \param ui32Base 是 I2C 模块的基址。
    //! \param ui8SlaveAddr 7位从地址
    //! \param bReceive 标志、指示与从设备的通信类型
    //!
    //! 此函数用于配置 I2C 主设备在
    //! 总线的电流。 设置了\e bReceive 参数
    //! 若设置为\b true、则地址表示 I2C 主设备正在启动 a
    //! 从从器件读取;否则地址指示 I2C
    //! 主器件正在向从器件发起写入。
    //!
    //! \无返回。
    ////
    *****************
    void
    I2CMasterSlaveAddrSet (uint32_t ui32Base、uint8_t ui8SlaveAddr、
    bool bReceive)
    {
    //
    //检查参数。
    //
    assert (_I2CBaseValid (ui32Base));
    assert (!(ui8SlaveAddr & 0x80));
    
    //
    //设置主设备将与之通信的从设备的地址。
    //
    HWREG (ui32Base + I2C_O_MSA)=(ui8SlaveAddr << 1)| bReceive;
    }
    

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    感谢 CB1_MOBILE 让我们了解一下。 我也许应该删除这些注释、因为它们会使代码更长。 我有一个可以用来检查信号的廉价逻辑分析仪。 我只是想、如果我的代码中存在缺失或不正确的内容、我会提出一些问题。

    此致
    Michael
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Michael -请注意供应商 Charles 的深刻见解。   (也是我的朋友)   他我们的外人更了解这些芯片-我没有(探究)可能"丢失"奴隶地址!   这是一个很大的错误-就我而言。   查尔斯  来到我们的救援中,这真是太棒了。

    减去正确的从器件地址-即使是"VERY BEST 示波器和上拉电阻器"-也不能为您节省费用!    (虽然它们不会伤害!)   向供应商的 Charles 致敬!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    我已将从器件地址更改为#define SLAVE_ADDRESS 0x21并设置断点我可以在 I2C_MSA 寄存器中看到正确的写入和读取地址。但我在任何时候都无法在 I2C_MDR 寄存器中看到用于标题信息的命令"A"。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您似乎是完全通过 MCU 的寄存器提取该数据。   MCU (可能)已生成(正确)数据(免刑)、这一事实表明(不允许)该数据已安全、正确地到达并由 I2C 从设备处理。

    同样、即使是简单的(偶数套)示波器、也会影响 MCU 寄存器的读取-作为诊断帮助。

    在这些(如此有限)诊断条件下-您能否找到"最简单的从机寄存器"-当再次寻址时、它启用了其(后续)最简短的(单字节)输出?    您的"此类从属数据的覆盖面"-将"杀死许多诊断鸟类-使用单个石头!"   如果您可以寻址-然后恢复从机数据-您已经验证(至少对于该事务)-"所有操作(可能)都是正确的、信号和协议。"   (法律学校教授可能的"对冲"。)

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    在代码中、您正在等待从器件接收数据。 但是、您并不像复制代码的示例那样处于回送模式。 从机将不接收数据。 CB1建议、范围是诊断问题的最佳工具之一。


    I2CMasterControl (I2C0_BASE、I2C_MASTER_CMD_SINGLE_SEND);
    while (!(I2CSlaveStatus (I2C0_BASE)& I2C_SLAVE_ACT_RREQ))



    请尝试一下、看看它是否会产生影响。
    I2CMasterControl (I2C0_BASE、I2C_MASTER_CMD_SINGLE_SEND);
    SysCtlDelay (100);
    while (I2CMasterBusy (I2C0_BASE))

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    大家好、

    我在某种程度上是因为您提醒我已经从 master_slave_loopback 样本复制了代码。 我发现本 文档 非常适合于解释 I2C.c 中的功能
    它还具有各种活动的信号应显示的详细图像。

    这有点像让它半途工作、并想知道数据为什么看起来很奇怪。 我一直得到相同的数字两次。 比如55 66或77。 这只是返回到这两个位中的第一个字节
    字节、因为我认为 I2C_MASTER_CMD_SINGLE_Receive 不能在循环中用于接收2个字节。 如果我错了、请纠正我的问题。 但它现在使用 I2C_MASTER_CMD_BURST_Receive_start 和 I2C_MASTER_CMD_BURST_Receive_finish 接收各个字节。

    代码可能需要整理、但在这里、任何其他人都应该寻求有关使用 TM4C 和 HMC6352指南针的帮助。 接下来、我想将计算如何将其写为
    非阻塞代码段。 有任何提示吗?

    //
    // HMC6352.c 与 Tiva TM4C1233H6PM 或 LM4F120HQ5R 配合使用 Tivaware 库
    //从罗盘读取2个字节的标题数据并打印到终端
    //该值将介于0和3599之间,并且精确到十分之一度
    //
    //
    #include
    #include
    #include "inc/hw_i2c.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/gpio.h"
    #include "driverlib/i2c.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"

    //
    //要接收的 I2C 数据包的数量。
    //
    #define NUM_I2C_DATA 2.
    //
    //设置从机模块的地址。 这是在中发送的7位地址
    //以下格式:
    //                     [A6:A5:A4:A3:A2:A1:A0:RS]
    //
    //第一个字节"RS"位置的零表示主器件
    //将数据发送(发送)到所选的从器件,并在该位置发送一个数据
    //表示主器件从从器件接收数据。
    //
    //
    #define SLAVE_ADDRESS 0x21
    //
    //
    //此函数将 UART0设置为用于控制台显示信息
    //因为示例正在运行。
    //
    //
    无效
    InitConsole (空)

       //
       //启用用于 UART0引脚的 GPIO 端口 A。
       // TODO:将其更改为您正在使用的 GPIO 端口。
       //
       SysCtlPeripheralEnable (SYSCTL_Periph_GPIOA);
       //
       //为端口 A0和 A1上的 UART0功能配置引脚复用。
       //如果您的器件不支持引脚复用、则无需执行此步骤。
       // TODO:更改此选项以选择您正在使用的端口/引脚。
       //
       GPIOPinConfigure (GPIO_PA0_U0RX);
       GPIOPinConfigure (GPIO_PA1_U0TX);
       //
       //启用 UART0以便我们可以配置时钟。
       //
       SysCtlPeripheralEnable (SYSCTL_Periph_UART0);
       //
       //使用内部16MHz 振荡器作为 UART 时钟源。
       //
       UARTClockSourceSet (UART0_BASE、UART_CLOCK_PIOSC);
       //
       //为这些引脚选择替代(UART)功能。
       // TODO:更改此选项以选择您正在使用的端口/引脚。
       //
       GPIOPinTypeUART (GPIO_Porta_base、GPIO_PIN_0 | GPIO_PIN_1);
       //
       //初始化控制台 I/O 的 UART
       //
       UARTStdioConfig (0、115200、16000000);

    //
    //
    //配置 I2C0主机。
    //
    //
    内部
    main (空)

       uint32_t pui32CmdTx;
       uint16_t pui16DataRx[NUM_I2C_DATA];
       uint16_t pu16标题;

       SysCtlClockSet (SYSCTL_SYSDIV_10 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                      SYSCTL_XTAL_16MHz);
       //
       //使用前必须启用 I2C0外设。
       //
       SysCtlPeripheralEnable (SYSCTL_Periph_I2C0);
       //
       //复位模块
       //
       SysCtlPeripheralReset (SYSCTL_Periph_I2C0);
       //
       //对于本示例,I2C0与 PortB[3:2]一起使用。  实际端口和
       //使用的引脚可能与您的器件不同、请参阅的数据表
       //更多信息。  GPIO 端口 B 需要启用、因此这些引脚可以
       //使用。
       // TODO:将其更改为您正在使用的 GPIO 端口。
       //
       SysCtlPeripheralEnable (SYSCTL_Periph_GPIOB);
       //
       //为端口 B2和 B3上的 I2C0功能配置引脚复用。
       //如果您的器件不支持引脚复用、则无需执行此步骤。
       // TODO:更改此选项以选择您正在使用的端口/引脚。
       //
       GPIOPinConfigure (GPIO_PB2_I2C0SCL);
       GPIOPinConfigure (GPIO_PB3_I2C0SDA);
       //
       //为这些引脚选择 I2C 功能。  此函数也会
       //为 I2C 操作配置 GPIO 引脚,将其设置为
       //开漏操作,弱上拉。  请参阅数据表
       //查看每个引脚分配了哪些功能。
       // TODO:更改此选项以选择您正在使用的端口/引脚。
       //
       GPIOPinTypeI2CSCL (GPIO_PORTB_BASE、GPIO_PIN_2);
       GPIOPinTypeI2C (GPIO_PORTB_BASE、GPIO_PIN_3);
       //
       //启用和初始化 I2C0主机模块。  使用的系统时钟
       // I2C0模块。  最后一个参数设置 I2C 数据传输速率。
       //如果为 false,则数据速率设置为100kbps,如果为 true,则数据速率将设置为
       //设置为400kbps。  在本示例中、我们将使用100kbps 的数据速率。
       //
       I2CMasterInitExpClk (I2C0_BASE、SysCtlClockGet ()、false);

       SysCtlDelay (10000);
       //
       //告诉主模块何时将在总线上放置什么地址
       //与从设备通信。  将地址设置为 SLAVE_ADDRESS
       //(在从机模块中设置)。  接收参数设置为 false
       //表示 I2C 主设备正在向从设备发起写入操作。  如果
       // true、这表示 I2C 主设备正在启动读取
       //从器件。
       //
       I2CMasterSlaveAddrSet (I2C0_BASE、SLAVE_ADDRESS、FALSE);
       //
       //设置用于显示消息的串行控制台。  这是
       //仅用于此示例程序,I2C 操作不需要。
       //
       InitConsole();
       //
       //在控制台上显示示例设置。
       //
       UARTprintf ("标题->");
       
       pui32CmdTx = 0x41;
       //
       //将要发送的数据放在数据寄存器中
       //
       I2CMasterDataPut (I2C0_BASE、pui32CmdTx);
       SysCtlDelay (1000);
       //
       //开始从主器件发送数据。
       //
       I2CMasterControl (I2C0_BASE、I2C_MASTER_CMD_BURST_SEND_START);
       //SysCtlDelay (1000);
       //
       //等待从机接收并确认数据。
       //
       while (I2CMasterBusy (I2C0_BASE));

       while (1)
       {
           //
           //将数据方向修改为 true,以便查看地址
           //表示 I2C 主设备正在从设备发起读取。
           //
           I2CMasterSlaveAddrSet (I2C0_BASE、SLAVE_ADDRESS、TRUE);
           //
          //告诉主设备读取数据。
          //
          I2CMasterControl (I2C0_BASE、I2C_MASTER_CMD_BURST_Receive_start);
           //
           //等待从机完成发送数据。
           //
          while (I2CMasterBusy (I2C0_BASE));
          //
           //从主设备读取数据。
           //
           pui16DataRx[0]= I2CMasterDataGet (I2C0_BASE);
          //
           //显示从机已接收到的数据。
           //
           I2CMasterControl (I2C0_BASE、I2C_MASTER_CMD_BURST_Receive_finish);

           while (I2CMasterBusy (I2C0_BASE));

           pui16DataRx[1]= I2CMasterDataGet (I2C0_BASE);

           pui16Heading = pui16DataRx[0]* 256 + pui16DataRx[1];

           UARTprintf ("%d"、pui16Heading);

           UARTprintf ("\n");

           SysCtlDelay (5000000);
       }