Hello again. I've got another question about the TM4C1294NCPDT / Tiva Connected Launchpad.
I'm prototyping a small expansion board for a Honeywill HIH-6121-021-001 temperature/humidity sensor over i2c. The Connected Launchpad pinout has an ideal spot for this on boosterpack 1 pins PL3, PL2, PL1, and PL0.
Layout
The HIH-6121 datasheet gives the pinout (p. 12 fig. 6).
I'm using PL3 as GPIO to power cycle the device. For 10 ms after power up the device can be put in "command mode."
- Vdd (3.3V) --- PL3 as GPIO; max current is 1mA (p. 8 table 4)
- Vss (GND) --- PL2 as GPIO
- SCL ------------ PL1 as i2c #2
- SDA ------------ PL0 as i2c #2
I'm prototyping it by connecting 4.7K ohm pull ups on PL1 and PL0, tied to PL3.
Problem
The I2C2 controller takes forever to get out of BUSY status on a very basic i2c command, the measurement request -- HIH-6121 i2c datasheet fig. 2 -- what should happen on the i2c bus:
- i2c master: START
- i2c master: 8 bits with LSB=0 for write and 7-bit address of 0x27
- i2c slave: ACK
- i2c master: STOP
What I'm seeing is that "done, err=00" is printed after at least 5 minutes (I don't know exactly how long it waits).
Code
Here's a simple example of what I'm doing. Once hih_flag & 4 is set, ROM_I2CMasterBusy() doesn't return 0 for quite a while.
I've tried a different i2c address, but the result is the same. Since no i2c slave is at that address I expect to see a NACK but I get "done, err=00".
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_adc.h"
#include "inc/hw_types.h"
#include "inc/hw_udma.h"
#include "inc/hw_emac.h"
#include "inc/hw_i2c.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/adc.h"
#include "driverlib/udma.h"
#include "driverlib/emac.h"
#include "driverlib/i2c.h"
//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void __error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif
void UARTsend(char * str)
{
while (*str) {
ROM_UARTCharPut(UART0_BASE, *str++);
}
}
volatile uint32_t hih_flag = 0;
void UART0IntHandler()
{
uint32_t status = ROM_UARTIntStatus(UART0_BASE, true);
ROM_UARTIntClear(UART0_BASE, status);
while (ROM_UARTCharsAvail(UART0_BASE)) {
char c = (char) ROM_UARTCharGetNonBlocking(UART0_BASE);
ROM_UARTCharPutNonBlocking(UART0_BASE, c);
if (c == 'h') {
hih_flag |= 1;
}
}
}
const uint8_t lookup_hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
void u32tohex(char * out, uint32_t n)
{
out[0] = lookup_hex[n >> 28]; out[1] = lookup_hex[(n >> 24) & 15]; out[2] = lookup_hex[(n >> 20) & 15]; out[3] = lookup_hex[(n >> 16) & 15];
out[4] = lookup_hex[(n >> 12) & 15]; out[5] = lookup_hex[(n >> 8) & 15]; out[6] = lookup_hex[(n >> 4) & 15]; out[7] = lookup_hex[n & 15];
}
void u16tohex(char * out, uint32_t n)
{
out[0] = lookup_hex[(n >> 12) & 15]; out[1] = lookup_hex[(n >> 8) & 15]; out[2] = lookup_hex[(n >> 4) & 15]; out[3] = lookup_hex[n & 15];
}
void u8tohex(char * out, uint32_t n)
{
out[0] = lookup_hex[(n >> 4) & 15]; out[1] = lookup_hex[n & 15];
}
int main(void)
{
uint32_t sysclock;
do {
sysclock = ROM_SysCtlClockFreqSet(SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480, 120*1000*1000);
} while (!sysclock);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2); // I2C #2 is on GPIOL pins PL0 (SDA), PL1 (SCL), but note that PL0 and PL1 are set to hi-Z initially
ROM_SysCtlDelay(1);
// configure PORTF for GPIO
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4);
ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1, 0);
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1);
ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1, GPIO_PIN_0);
ROM_GPIOPinTypeGPIOInput(GPIO_PORTL_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3); // set PL0, PL1, PL2, PL3 to hi-Z initially
ROM_I2CMasterEnable(I2C2_BASE);
ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
ROM_UARTConfigSetExpClk(UART0_BASE, sysclock, 115200, UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE);
ROM_IntMasterEnable();
ROM_IntEnable(INT_UART0);
ROM_UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);
uint8_t hih_init = 2;
uint8_t hih_address = 0x27;
for (;;) {
char str[16];
if (hih_init > 1) {
UARTsend("press h to init ");
u8tohex(str, hih_address);
str[2] = 0;
UARTsend(str);
UARTsend(": ");
hih_init--;
}
if (hih_flag && hih_init) {
hih_init = 0;
ROM_GPIOPinTypeI2C(GPIO_PORTL_BASE, GPIO_PIN_0);
ROM_GPIOPinTypeI2CSCL(GPIO_PORTL_BASE, GPIO_PIN_1);
ROM_GPIOPinConfigure(GPIO_PL0_I2C2SDA); // PL0 = HIH6120 pin 4 (SDA)
ROM_GPIOPinConfigure(GPIO_PL1_I2C2SCL); // PL1 = HIH6120 pin 3 (SCL)
ROM_I2CMasterInitExpClk(I2C2_BASE, sysclock, false /*100kHz clock rate*/);
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTL_BASE, GPIO_PIN_2 | GPIO_PIN_3); // PL2 = HIH6120 pin 2 (Vss = GND)
ROM_GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_2 | GPIO_PIN_3, GPIO_PIN_3); // PL3 = HIH6120 pin 1 (Vdd = 3.3V)
// note: at this point, PL3 supplies 3.3V to the HIH6120 so it is now powered up
UARTsend(" power on\r\n");
}
if (hih_flag & 1) {
hih_flag &= ~1;
if (!(hih_flag & 4)) {
hih_flag |= 4;
ROM_I2CMasterSlaveAddrSet(I2C2_BASE, hih_address, false /*write to slave*/);
while (ROM_I2CMasterBusy(I2C2_BASE)) {}
// which command? QUICK_COMMAND or SINGLE_SEND?
#if 1
ROM_I2CMasterControl(I2C2_BASE, I2C_MASTER_CMD_QUICK_COMMAND); // just send address and look for ACK
#else
ROM_I2CMasterControl(I2C2_BASE, I2C_MASTER_CMD_SINGLE_SEND); // just send address and look for ACK
#endif
while (ROM_I2CMasterBusy(I2C2_BASE)) {}
u8tohex(str, ROM_I2CMasterErr(I2C2_BASE));
str[2] = '\r'; str[3] = '\n'; str[4] = 0;
UARTsend("wait for ack. err=");
UARTsend(str);
}
}
if ((hih_flag & 4) && !ROM_I2CMasterBusy(I2C2_BASE)) {
hih_flag &= ~4;
u8tohex(str, ROM_I2CMasterErr(I2C2_BASE));
str[2] = '\r'; str[3] = '\n'; str[4] = 0;
UARTsend(" done, err=");
UARTsend(str);
}
}
}