I'm Noh.
I'm trying to control Microcontroller with C code. And I'm using a code I got from GitHub. (Here's the link https://github.com/jwr/msp430_usi_i2c)
I encountered the problem that main function doesn't stop. Even though there's no loop command in main, command flow repeats again and again.
I write command flow like below.
Write 00 → Write AF → Write 00 → Write 05 → Write 45 → Write 40 → Write 03 → Write B0 → Read EE
And you can see the command flow repeats from beginning to end.
Please refer to below code and the communication result.
#include <msp430.h>
#include <stdint.h>
#include "usi_i2c.h"
#include <stdio.h>
#include <stdlib.h>
// Internal state
static uint16_t const *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 uint8_t status;
static inline void i2c_prepare_stop();
static inline void i2c_prepare_data_xmit_recv();
void i2c_send_sequence(uint16_t const * 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
while((status==0xEE)|(status==0xEF)) P1OUT |= 0x01;
P1OUT &= ~0x01;
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
(*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--;
}
}
#ifdef __GNUC__
__attribute__((interrupt(USI_VECTOR)))
#else
#pragma vector = USI_VECTOR
__interrupt
#endif
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 into USISRL
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 > 0) {
// 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 main(){
i2c_init(USIDIV_5, USISSEL_2);
uint16_t PUP[] = {0xEA, 0x00};
uint16_t PDWN[] = {0xEA, 0x20};
uint16_t CVOL_PowerUp[] = {0xEA, 0xAF, 0x00};
uint16_t AMODE_PowerUp[] = {0xEA, 0x05, 0x45};
uint16_t AMODE_PowerDown[] = {0xEA, 0x05, 0x41};
uint16_t RDSTAT_Command[] = {0xEA, 0xB0};
uint16_t RDSTAT_Read[] = {0xEB, I2C_READ};
uint16_t START[] = {0xEA, 0x51};
uint16_t PLAY[] = {0xEA, 0x40, 0x03};
uint16_t STOP[] = {0xEA, 0x61};
i2c_send_sequence(PUP, 2, &status, LPM0_bits); // 00
i2c_send_sequence(CVOL_PowerUp, 3, &status, LPM0_bits); // AF 00
i2c_send_sequence(AMODE_PowerUp, 3, &status, LPM0_bits); // 05 45
i2c_send_sequence(PLAY, 3, &status, LPM0_bits); // 40 03
i2c_send_sequence(RDSTAT_Command, 2, &status, LPM0_bits); // B0
i2c_send_sequence(RDSTAT_Read, 2, &status, LPM0_bits);
}
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();
P1OUT = 0xC0; // P1.6 & P1.7 Pullups, others to 0
P1REN |= 0xC0; // P1.6 & P1.7 Pullups
P1DIR = 0xFF; // Unused pins as outputs
P2OUT = 0;
P2DIR = 0xFF;
}
I have no idea why main function repeats without any loop function.
I want this code run main function's command flow only once.
Do you happen to know why this happens and how to resolve this problem? 😥
Thank you!
Sincerely Noh
Most programs for a microcontroller have some assembly language initialisation code that does some basic configuration of the MCU, copies the data section from flash to RAM, zeros the .bss, then jumps to main().
Typically, main() would never return. It would consist of an infinite loop, doing whatever tasks the software is meant to do, forever, until power is removed.
So your code returning is not very typical. What is the MCU meant to do if main() returns? It has nothing else to run. Some options for it might be to:
It sounds like, in your case, either 2 or 3 is happening.
If you want your code to just stop after it has done its thing, add an empty infinite loop at the end of main():
for (;;)
;