waterlogged 0 Posted September 24, 2014 Share Posted September 24, 2014 (edited) Hey all,I've been staring at this for a while and for the life of me I can't figure it out. I've been trying to adapt the library here:http://jan.rychter.com/enblog/msp430-i2c-usi-library-releasedfor the F2003, which should be rather straightforward, as most of the register names and functions are similar. However, I'm able to get it to build and compile, but when the code gets around to the first send command, it goes into low power mode and never actually sends it. The pins are set, the LPM0 bits are correct, the interrupt vector name is correct, and a number of other things seem to go as anticipated. It just never actually bothers to start sending the data. I have also tried this with TI sample code, with the exact same results.I don't have a logic analyzer, but from hooking an LED up to one of the pins (it's not on there when I actually attempt communication), I can be almost certain that it never attempts to send data. It appears both SDA and SCL are kept low the entire time.Thank you, and any help is appreciated. Here's main.cpp #include <msp430.h> #include "usi_i2c.h" //define addresses #define MAG_ADDR 0xE /* * main.c */ int main(void) { WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; __enable_interrupt(); //some sanity checking P1DIR |= BIT0; P1OUT |= BIT0; P1OUT &= ~(BIT0); //init i2c SD16AE &= ~(BIT6 | BIT7); i2c_init(USIDIV_5, USISSEL_2); uint8_t stat = 0; uint16_t MAG3110_INIT_SEQ1[] = {0x1C, 0x11, 0xFF}; uint16_t MAG3110_INIT_SEQ2[] = {0x1C, 0x10, 0x81}; uint16_t MAG3110_READZ[] = {0x1C, 0x02, I2C_RESTART, 0x1D, I2C_READ}; i2c_send_sequence(MAG3110_INIT_SEQ1,3,&stat,LPM0_bits); LPM0; _NOP(); i2c_send_sequence(MAG3110_INIT_SEQ2,3,&stat,LPM0_bits); LPM0; while (1) { P1OUT ^= BIT0; int i; for (i=0; i<0x3000; i++); } } and here's the slightly modified usi_i2c.cpp thanks to Jan Rychter /* usi_i2c.c Copyright (C) 2013 Jan Rychter Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include <msp430.h> #include <stdint.h> #include "usi_i2c.h" // Internal state static uint16_t *i2c_sequence; static uint16_t i2c_sequence_length; static uint8_t *i2c_receive_buffer; static uint16_t i2c_wakeup_sr_bits; i2c_state_type i2c_state = I2C_IDLE; static inline void i2c_prepare_stop(); static inline void i2c_prepare_data_xmit_recv(); void i2c_send_sequence(uint16_t *sequence, uint16_t sequence_length, uint8_t *received_data, uint16_t wakeup_sr_bits) { while(i2c_state != I2C_IDLE); // we can't start another sequence until the current one is done i2c_sequence = sequence; i2c_sequence_length = sequence_length; i2c_receive_buffer = received_data; i2c_wakeup_sr_bits = wakeup_sr_bits; i2c_state = I2C_START; USICTL1 |= USIIFG; // actually start communication } static inline void i2c_prepare_stop() { USICTL0 |= USIOE; // SDA = output USISRL = 0x00; USICNT |= 0x01; // Bit counter= 1, SCL high, SDA low i2c_state = I2C_STOP; } static inline void i2c_prepare_data_xmit_recv() { if(i2c_sequence_length == 0) { i2c_prepare_stop(); // nothing more to do, prepare to send STOP } else { if(*i2c_sequence == I2C_RESTART) { USICTL0 |= USIOE; // SDA = output USISRL = 0xff; // prepare and send a dummy bit, so that SDA is high USICNT = (USICNT & 0xE0) | 1; i2c_state = I2C_START; } else if(*i2c_sequence == I2C_READ) { USICTL0 &= ~USIOE; // SDA = input USICNT = (USICNT & 0xE0) | 8; // Bit counter = 8, RX data i2c_state = I2C_RECEIVED_DATA; // next state: Test data and ACK/NACK } else { // a write // at this point we should have a pure data byte, not a command, so (*i2c_sequence >> 8) == 0 USICTL0 |= USIOE; // SDA = output USISRL = (char)(*i2c_sequence); // Load data byte USICNT = (USICNT & 0xE0) | 8; // Bit counter = 8, start TX i2c_state = I2C_PREPARE_ACKNACK; // next state: prepare to receive data ACK/NACK } i2c_sequence++; i2c_sequence_length--; } } #pragma vector = USI_VECTOR __interrupt void USI_TXRX(void) { switch(__even_in_range(i2c_state,12)) { case I2C_IDLE: break; case I2C_START: // generate start condition USISRL = 0x00; USICTL0 |= (USIGE|USIOE); USICTL0 &= ~USIGE; i2c_prepare_data_xmit_recv(); break; case I2C_PREPARE_ACKNACK: // prepare to receive ACK/NACK USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter=1, receive (N)Ack bit i2c_state = I2C_HANDLE_RXTX; // Go to next state: check ACK/NACK and continue xmitting/receiving if necessary break; case I2C_HANDLE_RXTX: // Process Address Ack/Nack & handle data TX if((USISRL & BIT0) != 0) { // did we get a NACK? i2c_prepare_stop(); } else { i2c_prepare_data_xmit_recv(); } break; case I2C_RECEIVED_DATA: // received data, send ACK/NACK *i2c_receive_buffer = USISRL; i2c_receive_buffer++; USICTL0 |= USIOE; // SDA = output if(i2c_sequence_length > 1) { // If this is not the last byte USISRL = 0x00; // ACK i2c_state = I2C_HANDLE_RXTX; // Go to next state: data/rcv again } else { // last byte: send NACK USISRL = 0xff; // NACK i2c_state = I2C_PREPARE_STOP; // stop condition is next } USICNT |= 0x01; // Bit counter = 1, send ACK/NACK bit break; case I2C_PREPARE_STOP: // prepare stop condition i2c_prepare_stop(); // prepare stop, go to state 14 next break; case I2C_STOP: // Generate Stop Condition USISRL = 0x0FF; // USISRL = 1 to release SDA USICTL0 |= USIGE; // Transparent latch enabled USICTL0 &= ~(USIGE+USIOE); // Latch/SDA output disabled i2c_state = I2C_IDLE; // Reset state machine for next xmt if(i2c_wakeup_sr_bits) { _bic_SR_register_on_exit(i2c_wakeup_sr_bits); // exit active if prompted to } break; } USICTL1 &= ~USIIFG; // Clear pending flag } void i2c_init(uint16_t usi_clock_divider, uint16_t usi_clock_source) { _disable_interrupts(); USICTL0 = USIPE6+USIPE7+USIMST+USISWRST; // Port & USI mode setup USICTL1 = USII2C+USIIE; // Enable I2C mode & USI interrupt USICKCTL = usi_clock_divider + usi_clock_source + USICKPL; USICNT |= USIIFGCC; // Disable automatic clear control USICTL0 &= ~USISWRST; // Enable USI USICTL1 &= ~USIIFG; // Clear pending flag _enable_interrupts(); } Edited September 24, 2014 by waterlogged Quote Link to post Share on other sites
bluehash 1,581 Posted October 2, 2014 Share Posted October 2, 2014 Bump. Quote Link to post Share on other sites
roadrunner84 466 Posted October 3, 2014 Share Posted October 3, 2014 The code seems kinda okay, I can't find any weird things so far. So, maybe a crude question; do you have pull-up resistors installed on your I2C lines? Quote Link to post Share on other sites
waterlogged 0 Posted October 4, 2014 Author Share Posted October 4, 2014 BlueHash, thanks for the bump. I'm always reluctant to do it myself... Roadrunner, yep. I've tried a couple of values of pull-up resistor, and have left 4.7k resistors in. I have the circuit working using a parallax propeller board. In the time since I've posted, I also tried creating my own i2c code, and the chip hangs in the same place; right when I tell it to start transferring and start waiting for the interrupt saying it is finished. Any ideas for tests or fixes would be extremely appreciated. Quote Link to post Share on other sites
zeke 693 Posted October 5, 2014 Share Posted October 5, 2014 I have been frustrated so much by the interrupt drive iic master side that I now just bit bang the whole stinking thing - no interrupts at all. Do you have the Davies book? It will cover the 200x series iic port with sample code. Do you have a logic analyzer? It would be nice to see the waveforms on the clock and data pins. Can you single step debug your way through the code and examine the registers for expected values? I do not see where your code looks for ACK bits from the slave. Maybe I am blind tonight. Quote Link to post Share on other sites
waterlogged 0 Posted October 8, 2014 Author Share Posted October 8, 2014 Zeke, I don't have the book, but I'll see if I can grab a copy. Unfortunately, no logic analyzer either. I have stepped through the code a couple of times. It goes into this call: i2c_send_sequence(MAG3110_INIT_SEQ1,3,&stat,LPM0_bits); And the switch case in usi_i2c.cpp goes through the first case and the second case ( I2C_START ) and freezes immediately after. If I pause while its frozen, it comes out at the call to go into the LPM0 call in main. Quote Link to post Share on other sites
zeke 693 Posted October 8, 2014 Share Posted October 8, 2014 Well, my intuition tells me to check the following: 1. Verify the correct iic address for the slave is being sent. 2. Examine the operation of the state machine by single stepping through it in debug mode 3. Examine the operation of the state machine helper functions by putting breakpoints at key decision lines of code. I immediately want to check out the operation of i2c_prepare_data_xmit_recv() to see what it is doing. Is it getting a stop condition right away? Next, I would verify the voltage levels of the SDA and SCL lines when I think the code has frozen. Just use a multimeter to see what voltage they are at. If the bus has frozen then their states will give us clues why. A logic analyzer can be had for little money these days. Seeedstudio sells the DangerousPrototypes version at an affordable price. Check aliexpress too. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.