[英]MSP430 I²C Bus: Phase between clock and data
I am currently trying to use the TI MSP430 sample code to send commands from my MSP430FR2512 microcontroller to a PCA9955B LED driver.我目前正在尝试使用 TI MSP430 示例代码将命令从我的 MSP430FR2512 微控制器发送到PCA9955B LED 驱动器。 Unfortunately, I don't receive an acknowledgment from the driver (address is 0x01).
不幸的是,我没有收到驱动程序的确认(地址是 0x01)。 It seems like clock and SDA are half a period shifted and not by 1/4 period as shown in a TI document :
时钟和 SDA 似乎偏移了半个周期,而不是TI 文档中所示的 1/4 周期:
TI I2C Timing Diagram TI I2C 时序图
Measured I2C Communication测量 I2C 通信
Is this normal?这是正常的吗?
This is the slightly changed example code I used (essentially only using the init and master write functions):这是我使用的稍微改动过的示例代码(基本上只使用 init 和 master write 函数):
//******************************************************************************
// MSP430FR2422 Demo - eUSCI_B0, I2C Master multiple byte TX/RX
//
// Description: I2C master communicates to I2C slave sending and receiving
// 3 different messages of different length. I2C master will enter LPM0 mode
// while waiting for the messages to be sent/receiving using I2C interrupt.
// ACLK = NA, MCLK = SMCLK = DCO 16MHz.
//
// /|\ /|\
// MSP430FR2422 4.7k |
// ----------------- | 4.7k
// /|\ | P1.3|---+---|-- I2C Clock (UCB0SCL)
// | | | |
// ---|RST P1.2|-------+-- I2C Data (UCB0SDA)
// | |
// | |
// | |
// | |
// | |
// | |
//
// Lixin Chen and Xiaodong Li
// Texas Instruments Inc.
// Dec 2020
// Built with CCS V10.1
//******************************************************************************
#include <msp430.h>
#include <stdint.h>
#include <stdbool.h>
//******************************************************************************
// Pin Config ******************************************************************
//******************************************************************************
#define LED_OUT P1OUT
#define LED_DIR P1DIR
#define LED0_PIN BIT0
#define LED1_PIN BIT1
//******************************************************************************
// Example Commands ************************************************************
//******************************************************************************
#define SLAVE_ADDR 0x01
/* CMD_TYPE_X_SLAVE are example commands the master sends to the slave.
* The slave will send example SlaveTypeX buffers in response.
*
* CMD_TYPE_X_MASTER are example commands the master sends to the slave.
* The slave will initialize itself to receive MasterTypeX example buffers.
* */
#define CMD_TYPE_0_SLAVE 0
#define CMD_TYPE_1_SLAVE 1
#define CMD_TYPE_2_SLAVE 2
#define CMD_TYPE_2_MASTER 5
#define TYPE_0_LENGTH 1
#define TYPE_1_LENGTH 1
#define TYPE_2_LENGTH 1
#define MAX_BUFFER_SIZE 20
/* MasterTypeX are example buffers initialized in the master, they will be
* sent by the master to the slave.
* SlaveTypeX are example buffers initialized in the slave, they will be
* sent by the slave to the master.
* */
uint8_t Message1_Reg = 0x02;
uint8_t Message2_Reg = 0x45;
uint8_t Message1 [TYPE_2_LENGTH] = {0x14}; // LED 1,2 fully on
uint8_t Message2 [TYPE_2_LENGTH] = {0xFF}; // All LEDs full gain
//uint8_t MasterType2 [TYPE_2_LENGTH] = {'F', '4', '1', '9', '2', 'B'};
//uint8_t MasterType1 [TYPE_1_LENGTH] = { 8, 9};
//uint8_t MasterType0 [TYPE_0_LENGTH] = { 11};
uint8_t SlaveType2 [TYPE_2_LENGTH] = {0};
uint8_t SlaveType1 [TYPE_1_LENGTH] = {0};
uint8_t SlaveType0 [TYPE_0_LENGTH] = {0};
//******************************************************************************
// General I2C State Machine ***************************************************
//******************************************************************************
typedef enum I2C_ModeEnum{
IDLE_MODE,
NACK_MODE,
TX_REG_ADDRESS_MODE,
RX_REG_ADDRESS_MODE,
TX_DATA_MODE,
RX_DATA_MODE,
SWITCH_TO_RX_MODE,
SWITHC_TO_TX_MODE,
TIMEOUT_MODE
} I2C_Mode;
/* Used to track the state of the software state machine*/
I2C_Mode MasterMode = IDLE_MODE;
/* The Register Address/Command to use*/
uint8_t TransmitRegAddr = 0;
/* ReceiveBuffer: Buffer used to receive data in the ISR
* RXByteCtr: Number of bytes left to receive
* ReceiveIndex: The index of the next byte to be received in ReceiveBuffer
* TransmitBuffer: Buffer used to transmit data in the ISR
* TXByteCtr: Number of bytes left to transfer
* TransmitIndex: The index of the next byte to be transmitted in TransmitBuffer
* */
uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0};
uint8_t RXByteCtr = 0;
uint8_t ReceiveIndex = 0;
uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0};
uint8_t TXByteCtr = 0;
uint8_t TransmitIndex = 0;
/* I2C Write and Read Functions */
/* For slave device with dev_addr, writes the data specified in *reg_data
*
* dev_addr: The slave device address.
* Example: SLAVE_ADDR
* reg_addr: The register or command to send to the slave.
* Example: CMD_TYPE_0_MASTER
* *reg_data: The buffer to write
* Example: MasterType0
* count: The length of *reg_data
* Example: TYPE_0_LENGTH
* */
I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t count);
/* For slave device with dev_addr, read the data specified in slaves reg_addr.
* The received data is available in ReceiveBuffer
*
* dev_addr: The slave device address.
* Example: SLAVE_ADDR
* reg_addr: The register or command to send to the slave.
* Example: CMD_TYPE_0_SLAVE
* count: The length of data to read
* Example: TYPE_0_LENGTH
* */
I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t count);
void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count);
bool i2c_write_byte(uint8_t byte);
I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t count)
{
/* Initialize state machine */
MasterMode = TX_REG_ADDRESS_MODE;
TransmitRegAddr = reg_addr;
RXByteCtr = count;
TXByteCtr = 0;
ReceiveIndex = 0;
TransmitIndex = 0;
/* Initialize slave address and interrupts */
UCB0I2CSA = dev_addr;
UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts
UCB0IE &= ~UCRXIE; // Disable RX interrupt
UCB0IE |= UCTXIE; // Enable TX interrupt
UCB0CTLW0 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts
return MasterMode;
}
I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t count)
{
/* Initialize state machine */
MasterMode = TX_REG_ADDRESS_MODE;
TransmitRegAddr = reg_addr;
//Copy register data to TransmitBuffer
CopyArray(reg_data, TransmitBuffer, count);
TXByteCtr = count;
RXByteCtr = 0;
ReceiveIndex = 0;
TransmitIndex = 0;
/* Initialize slave address and interrupts */
UCB0I2CSA = dev_addr;
UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts
UCB0IE &= ~UCRXIE; // Disable RX interrupt
UCB0IE |= UCTXIE; // Enable TX interrupt
UCB0CTLW0 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts
return MasterMode;
}
void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count)
{
uint8_t copyIndex = 0;
for (copyIndex = 0; copyIndex < count; copyIndex++)
{
dest[copyIndex] = source[copyIndex];
}
}
//******************************************************************************
// Device Initialization *******************************************************
//******************************************************************************
void initGPIO()
{
// Configure GPIO
//LED_OUT &= ~(LED0_PIN | LED1_PIN); // P1 setup for LED & reset output
//LED_DIR |= (LED0_PIN | LED1_PIN);
// I2C pins
P1OUT |= BIT2 | BIT3;
//P1REN |= BIT2 | BIT3;
P1SEL0 |= BIT2 | BIT3;
P1SEL1 &= ~(BIT2 | BIT3);
// I2C Level Shifter Enable
P1OUT &= ~BIT1; // Clear P1.1 output latch for a defined power-on state
P1DIR |= BIT1; // Set P1.1 to output direction
P1OUT |= BIT1; // Turn on P1.1
// 5V DCDC Converter Enable
P2OUT &= ~BIT2; // Clear P2.2 output latch for a defined power-on state
P2DIR |= BIT2; // Set P2.2 to output direction
P2OUT |= BIT2; // Turn on P2.2
// Disable the GPIO power-on default high-impedance mode to activate
// previously configured port settings
PM5CTL0 &= ~LOCKLPM5;
}
void initClockTo16MHz()
{
// Configure one FRAM waitstate as required by the device datasheet for MCLK
// operation beyond 8MHz _before_ configuring the clock system.
FRCTL0 = FRCTLPW | NWAITS_1;
// Clock System Setup
__bis_SR_register(SCG0); // disable FLL
CSCTL3 |= SELREF__REFOCLK; // Set REFO as FLL reference source
CSCTL0 = 0; // clear DCO and MOD registers
CSCTL1 &= ~(DCORSEL_7); // Clear DCO frequency select bits first
CSCTL1 |= DCORSEL_5; // Set DCO = 16MHz
CSCTL2 = FLLD_0 + 487; // DCOCLKDIV = 16MHz
__delay_cycles(3);
__bic_SR_register(SCG0); // enable FLL
while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // FLL locked
}
void initI2C()
{
UCB0CTLW0 = UCSWRST; // Enable SW reset
UCB0CTLW0 |= UCMODE_3 | UCMST | UCSSEL__SMCLK | UCSYNC; // I2C master mode, SMCLK
UCB0BRW = 160; // fSCL = SMCLK/160 = ~100kHz
UCB0I2CSA = SLAVE_ADDR; // Slave Address
UCB0CTLW0 &= ~UCSWRST; // Clear SW reset, resume operation
UCB0IE |= UCNACKIE;
}
//******************************************************************************
// Main ************************************************************************
// Send and receive three messages containing the example commands *************
//******************************************************************************
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
initClockTo16MHz();
initGPIO();
initI2C();
//I2C_Master_WriteReg(SLAVE_ADDR, CMD_TYPE_0_MASTER, MasterType0, TYPE_0_LENGTH);
//I2C_Master_WriteReg(SLAVE_ADDR, CMD_TYPE_1_MASTER, MasterType1, TYPE_1_LENGTH);
I2C_Master_WriteReg(SLAVE_ADDR, Message1_Reg, Message1, TYPE_2_LENGTH);
I2C_Master_WriteReg(SLAVE_ADDR, Message2_Reg, Message2, TYPE_2_LENGTH);
/*
I2C_Master_ReadReg(SLAVE_ADDR, CMD_TYPE_0_SLAVE, TYPE_0_LENGTH);
CopyArray(ReceiveBuffer, SlaveType0, TYPE_0_LENGTH);
I2C_Master_ReadReg(SLAVE_ADDR, CMD_TYPE_1_SLAVE, TYPE_1_LENGTH);
CopyArray(ReceiveBuffer, SlaveType1, TYPE_1_LENGTH);
I2C_Master_ReadReg(SLAVE_ADDR, CMD_TYPE_2_SLAVE, TYPE_2_LENGTH);
CopyArray(ReceiveBuffer, SlaveType2, TYPE_2_LENGTH);
*/
__bis_SR_register(LPM0_bits + GIE);
return 0;
}
//******************************************************************************
// I2C Interrupt ***************************************************************
//******************************************************************************
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
//Must read from UCB0RXBUF
uint8_t rx_val = 0;
switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
{
case USCI_NONE: break; // Vector 0: No interrupts
case USCI_I2C_UCALIFG: break; // Vector 2: ALIFG
case USCI_I2C_UCNACKIFG: // Vector 4: NACKIFG
break;
case USCI_I2C_UCSTTIFG: break; // Vector 6: STTIFG
case USCI_I2C_UCSTPIFG: break; // Vector 8: STPIFG
case USCI_I2C_UCRXIFG3: break; // Vector 10: RXIFG3
case USCI_I2C_UCTXIFG3: break; // Vector 12: TXIFG3
case USCI_I2C_UCRXIFG2: break; // Vector 14: RXIFG2
case USCI_I2C_UCTXIFG2: break; // Vector 16: TXIFG2
case USCI_I2C_UCRXIFG1: break; // Vector 18: RXIFG1
case USCI_I2C_UCTXIFG1: break; // Vector 20: TXIFG1
case USCI_I2C_UCRXIFG0: // Vector 22: RXIFG0
rx_val = UCB0RXBUF;
if (RXByteCtr)
{
ReceiveBuffer[ReceiveIndex++] = rx_val;
RXByteCtr--;
}
if (RXByteCtr == 1)
{
UCB0CTLW0 |= UCTXSTP;
}
else if (RXByteCtr == 0)
{
UCB0IE &= ~UCRXIE;
MasterMode = IDLE_MODE;
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
break;
case USCI_I2C_UCTXIFG0: // Vector 24: TXIFG0
switch (MasterMode)
{
case TX_REG_ADDRESS_MODE:
UCB0TXBUF = TransmitRegAddr;
if (RXByteCtr)
MasterMode = SWITCH_TO_RX_MODE; // Need to start receiving now
else
MasterMode = TX_DATA_MODE; // Continue to transmision with the data in Transmit Buffer
break;
case SWITCH_TO_RX_MODE:
UCB0IE |= UCRXIE; // Enable RX interrupt
UCB0IE &= ~UCTXIE; // Disable TX interrupt
UCB0CTLW0 &= ~UCTR; // Switch to receiver
MasterMode = RX_DATA_MODE; // State state is to receive data
UCB0CTLW0 |= UCTXSTT; // Send repeated start
if (RXByteCtr == 1)
{
//Must send stop since this is the N-1 byte
while((UCB0CTLW0 & UCTXSTT));
UCB0CTLW0 |= UCTXSTP; // Send stop condition
}
break;
case TX_DATA_MODE:
if (TXByteCtr)
{
UCB0TXBUF = TransmitBuffer[TransmitIndex++];
TXByteCtr--;
}
else
{
//Done with transmission
UCB0CTLW0 |= UCTXSTP; // Send stop condition
MasterMode = IDLE_MODE;
UCB0IE &= ~UCTXIE; // disable TX interrupt
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
break;
default:
__no_operation();
break;
}
break;
default: break;
}
}
On the custom PCB I have a direct connection from the MSP430 via a TCA9406 I2C level shifter with integrated pull-ups to the PCA9955B.在定制 PCB 上,我通过带有集成上拉电阻的 TCA9406 I2C 电平转换器从 MSP430 直接连接到 PCA9955B。 I measured on the side B of the level shifter.
我在电平转换器的 B 侧进行了测量。
Best, Josh最好的,乔希
I found my stupid mistake.我发现了我的愚蠢错误。 The PCA9955B requires an EXTERNAL pull-up resistor on its ~RESET pin.
PCA9955B 在其 ~RESET 引脚上需要一个外部上拉电阻。
PCA9955B Pin Description PCA9955B 引脚说明
I2C seems to be working fine. I2C 似乎工作正常。
Best, Josh最好的,乔希
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.