Jump to content

    

Проблема с I2C в MSP430F2617

Пришлось по необходимости заняться МК MSP430F2617. К счастью, для чайников в среде IAR есть примеры. Все примеры удалось запустить и отработать, кроме примера по I2C, хотя он там нужен. Привожу код в примере

#include "msp430x26x.h"

 

unsigned char TXData;

unsigned char TXByteCtr;

 

void main(void)

{

WDTCTL = WDTPW + WDTHOLD; // Stop WDT

P3SEL |= 0x06; // Assign I2C pins to USCI_B0

UCB0CTL1 |= UCSWRST; // Enable SW reset

UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode

UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset

UCB0BR0 = 12; // fSCL = SMCLK/12 = ~100kHz

UCB0BR1 = 0;

UCB0I2CSA = 0x48; // Slave Address is 048h

UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation

IE2 |= UCB0TXIE; // Enable TX interrupt

 

TXData = 0x01; // Holds TX data

 

while (1)

{

TXByteCtr = 1; // Load TX byte counter

while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent

UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition

__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts

// Remain in LPM0 until all data

// is TX'd

TXData++; // Increment data byte

}

}

 

//------------------------------------------------------------------------------

// The USCIAB0TX_ISR is structured such that it can be used to transmit any

// number of bytes by pre-loading TXByteCtr with the byte count.

//------------------------------------------------------------------------------

#pragma vector = USCIAB0TX_VECTOR

__interrupt void USCIAB0TX_ISR(void)

{

if (TXByteCtr) // Check TX byte counter

{

UCB0TXBUF = TXData; // Load TX buffer

TXByteCtr--; // Decrement TX byte counter

}

else

{

UCB0CTL1 |= UCTXSTP; // I2C stop condition

IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag

__bic_SR_register_on_exit(CPUOFF); // Exit LPM0

}

}

 

В комментарии указано: "Этот демо связывает два MSP430 по шине I2C. Мaster передает slave. Это код для master. Он постоянно передает 00h, 01h, ..., 0ffh и демонстрирует, как подключить I2C master transmitter для передачи одного байта using прерывание USCI_B0 TX." Так вот, я запустил этот пример, и он НЕ работает так, как написано в комментарии. В чем может быть дело?

Share this post


Link to post
Share on other sites

Во-первых, нужно напомнить, что шина I2C обязательно требует pull-up резисторов на обеих линиях (SCL и SDA).

Во-вторых, этот пример слишком упрощен. Автомат состояний шины I2C гораздо сложнее и в идеале его нужно реализовывать полностью (назначить действие для каждого возможного варианта состояния шины).

В-третьих, нужно обязательно реализовать функцию Bus clear, описанную в спецификации I2C, вызываемую по тайм-ауту неактивности занятой шины. С такой необходимостью мы сталкивались неоднократно и поэтому реализуем ее всегда. Как минимум один раз, после подачи питания на устройства, подключенные к шине I2C, функцию Bus clear необходимо вызывать всегда.

Share this post


Link to post
Share on other sites
Во-первых, нужно напомнить, что шина I2C обязательно требует pull-up резисторов на обеих линиях (SCL и SDA).

Во-вторых, этот пример слишком упрощен. Автомат состояний шины I2C гораздо сложнее и в идеале его нужно реализовывать полностью (назначить действие для каждого возможного варианта состояния шины).

В-третьих, нужно обязательно реализовать функцию Bus clear, описанную в спецификации I2C, вызываемую по тайм-ауту неактивности занятой шины. С такой необходимостью мы сталкивались неоднократно и поэтому реализуем ее всегда. Как минимум один раз, после подачи питания на устройства, подключенные к шине I2C, функцию Bus clear необходимо вызывать всегда.

 

Может, он и упрощен, но делать то, что указано в комментарии, он обязан (я так полагал). Но он вообще заходит в прерывание один раз, а потом "уходит в подполье", откуда его отладчиком IAR не вытащить. А эта функция BUS CLEAR, она описана в спецификации?

 

Очевидно, имеются в виду внешние - на плате - резисторы, подтягивающие провода SCK и SDA к напряжению питания, то есть к "1"?

Share this post


Link to post
Share on other sites
Может, он и упрощен, но делать то, что указано в комментарии, он обязан (я так полагал). Но он вообще заходит в прерывание один раз, а потом "уходит в подполье", откуда его отладчиком IAR не вытащить.
Поясните более понятно, что именно не работает? Активности на шине I2C никакой нету? Или дебаггер не работает? Если только второе, то уберите из программы все команды перехода в режим энергосбережения. То бишь команду

__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts

замените на

__bis_SR_register(GIE); // Enable interrupts

А эта функция BUS CLEAR, она описана в спецификации?
Да, конечно же. Раздел 3.16 Bus clear. Для выведения зависшего слейва из "ступора" и разблокирования шины SDA необходимо, не создавая старт- или стоп-условий, сгенерировать на шине SCL не менее 9 тактовых импульсов. Для этого в MSP430 удобнее перепрограммировать пин к которому подключена SCL как GPIO в режиме вывода и выдать последовательность из 9 тактовых импульсов частотой не выше 400кГц или 100кГц (в зависимости от типа подключенных устройств). После этого можно перевести пин обратно на функцию SCL и начать новую передачу.

Очевидно, имеются в виду внешние - на плате - резисторы, подтягивающие провода SCK и SDA к напряжению питания, то есть к "1"?
Да, внешние. Внутренние pull-up не позволяют работать на высокой скорости.

Share this post


Link to post
Share on other sites

Гм, все оказалось донельзя тривиально. Как только прицепили к SCK и SDA подтягивающие резисторы, обмен пошел. Есть, правда, некоторые шероховатости, но это уже отдельная тема. Спасибо за подсказку и дополнительные советы. Обязательно учтем.

Share this post


Link to post
Share on other sites
Гм, все оказалось донельзя тривиально. Как только прицепили к SCK и SDA подтягивающие резисторы, обмен пошел. Есть, правда, некоторые шероховатости, но это уже отдельная тема. Спасибо за подсказку и дополнительные советы. Обязательно учтем.

Слова "обмен пошел" не совсем точно отражают состояние дел. Пошщел в одну сторону - master->slave. Может быть, я спрашиваю, как истинный чайник, очевидные вещи, но тем не менее спрошу: как выдать на GPIO последовательность тактовых импульсов с определенной частотой (например, 400 кГц) - это все для BusClear.

Edited by Lemist

Share this post


Link to post
Share on other sites

А 100кГц не устроит? :)

#define PIN_SCL (1U<<2)

P3OUT &= PIN_SCL^0xFF;
P3DIR &= PIN_SCL^0xFF;
P3SEL &= PIN_SCL^0xFF;
for (unsigned int cntr=0; cntr<9; cntr++)
{ P3DIR |= PIN_SCL;
  __delay_us(5);
  P3DIR &= PIN_SCL^0xFF;
  __delay_us(5);
}
P3SEL |= PIN_SCL;
P3OUT |= PIN_SCL;

 

Функцию задержки на 5 мкс __delay_us(5) сами уж как-нибудь определите. Хотя бы требуемым количеством NOPов.

Share this post


Link to post
Share on other sites

Спасибо за код, должен сказать, что ничего военного в нем нет, и все достаточно прозрачно.

Впрочем, из примеров я сделал несколько выводов.

Во-первых, получается, что сам обмен включает, кроме передаваемых байтов, еще много чего (очевидно, все это организует встроенный в микроконтроллер модуль, реализующий скрытые функции I2C).

Например, в примерах (и, надо полагать, в реальных программах) нет передачи адреса - в лучшем случае он указывается в специальных регистрах при настройках протокола. А кто его тогда передает?

Нет там и "обрамления" передаваемых байтов (например, в адресной посылке, кроме самого 7-битного адреса, должен быть так называемый R/W бит, определяющий направление потока данных, а в обычной посылке 8 бит данных дополняются битом подтверждения). Их тоже наверняка формирует встроенный модуль.

Здесь для меня из примеров теперь непонятно следующее: я там нигде не нашел и намека на определение направления потока данных (то есть каким образом будет формироваться этот самый R/W бит). А это может быть определяющим для программирования обмена.

Например, в процессе работы с каким-либо устройством нужно запросить состояние определенного регистра. Для этого нужно послать запрос (ПЕРЕДАЧА) и перейти на прием. Это как-то нужно сделать программно.

Вот такие вопросы.

 

Share this post


Link to post
Share on other sites
Вот такие вопросы.
Как говориться в таких случаях RTFM! :) Ответы на все эти вопросы есть в MSP430x2xx Family User's Guide (Rev. E), чтением которого вы видимо пренебрегли? Если у вас проблемы с английским, то купите книжку-перевод этого руководства. В соседней теме ее рекламировали.

 

Share this post


Link to post
Share on other sites
Как говориться в таких случаях RTFM! :) Ответы на все эти вопросы есть в MSP430x2xx Family User's Guide (Rev. E), чтением которого вы видимо пренебрегли? Если у вас проблемы с английским, то купите книжку-перевод этого руководства. В соседней теме ее рекламировали.

 

Да нет, и с английским проблем у меня нет, и упомянутый вами документ имеется, я его вообще-то читал.

Впрочем, виноват, видимо, читал невнимательно. Сейчас прошелся по разделу I2C и таки нашел фразу RESTART, которой, очевидно, Master и должен менять направление потока данных.

Share this post


Link to post
Share on other sites

Извините, что столь неугомонен, но с обменом по протоколу I2C все-таки не все ладно. У меня возникло подозрение, что slave (а в качестве такового в обмене участвует акселерометр LIS331DLH) не отвечает, не дает подтверждение, необходимое по протоколу. Судя по примерам, вся последовательность обмена:

Start - Slave Address - R/W - ACK - Data - ACK - S Slave Address - R/W - ACK - Data - ACK - Stop

недоступна для программы - значит, существует "нижний" уровень типа микропрограммного, реализцющий собственно обмен. Об этом, кстати, свидетельствует и то, что в упомянутой Вами книге есть раздел "Universal Serial Interface", и регистры этого раздела (например, USICTLx или USICNT etc), однако из программы ни один из этих регистров недоступен, хотя в самой книге есть Ассемблерные примеры управления через эти регистры.

Как все-таки убедиться, что подтверждение со стороны приемника slave действительно приходит (или НЕ приходит)? Ведь в последнем случае обмена как раз и не будет.

Share this post


Link to post
Share on other sites
Об этом, кстати, свидетельствует и то, что в упомянутой Вами книге есть раздел "Universal Serial Interface", и регистры этого раздела (например, USICTLx или USICNT etc), однако из программы ни один из этих регистров недоступен, хотя в самой книге есть Ассемблерные примеры управления через эти регистры.

Уважаемый, если предположить, что у вас плохое зрение, то вы видимо забыли надеть очки? :cranky: Ну причем тут модуль USI (Universal Serial Interface) и соответствующий ему раздел в User's manual (Chapter 14), если в вашем кристалле модуль USCI (Universal Serial Communication Interface) и читать вам следует раздел Chapter 17. Universal Serial Communication Interface, I2C Mode? :laughing:

Share this post


Link to post
Share on other sites
Уважаемый, если предположить, что у вас плохое зрение, то вы видимо забыли надеть очки? :cranky: Ну причем тут модуль USI (Universal Serial Interface) и соответствующий ему раздел в User's manual (Chapter 14), если в вашем кристалле модуль USCI (Universal Serial Communication Interface) и читать вам следует раздел Chapter 17. Universal Serial Communication Interface, I2C Mode? :laughing:

 

Может, я и не о том говорил, но вопрос все равно остается. Как из программы отловить наличие подтверждения от приемника? Опять же никаких упоминаний (битов в регистрах) нет.

Share this post


Link to post
Share on other sites
Как из программы отловить наличие подтверждения от приемника? Опять же никаких упоминаний (битов в регистрах) нет.
Опять ловлю вас на невнимательном чтении документации.

If the slave does not acknowledge the transmitted data the not-acknowledge interrupt flag UCNACKIFG is set. The master must react with either a STOP condition or a repeated START condition. If data was already written into UCBxTXBUF it will be discarded. If this data should be transmitted after a repeated START it must be written into UCBxTXBUF again. Any set UCTXSTT is discarded, too. To trigger a repeated start UCTXSTT needs to be set again.

post-3882-1286548083_thumb.png

Share this post


Link to post
Share on other sites

Разработчики USART MSP430 2xx заложили в него 2 идеи, в общем-то спорные, но имеющие право на жизнь, без понимания которых вы не сможете понять все остальное:

(речь о режиме Master Transmitter/Receiver)

 

1 адрес Slave не является частью пакета данных, а задается отдельно. Это сильно упрощает программирование, если у Вас на шине одно устройство (частая ситуация). Вы один раз задаете адрес и можете забыть о нем, USCI сам вставляет его в начале каждого приема/передачи. Бит R/W при этом определяется битом UCTR.

 

2 Штатным ответом Slave является ACK, NACK - признак нештатной ситуации. По ACKу не выставляется никаких специальных флагов, просто контроллер дает знать о готовности к приему/передаче следующего байта через UCxxRXIFG/UCxxTXIFG, в случае получения NACK контроллер выставит UCNACKIFG ( вызовет прерывание), сотрет данные из передающего буфера, если они там есть, и будет ждать вашей реакции.

 

В общем, проще прислать код, наверное...

Вот кусок кода, только он для UCB1, надо будет подправить. Прерывания для приема,передачи не используются, NACK обрабатывается в ISR

Реализует как раз ваше:

Start - Slave Address - R/W - ACK - Data - ACK - S Slave Address - R/W - ACK - Data - ACK - Stop

 

void I2C_SetSlave(unsigned char slave_address, unsigned char baudrate)
{
  UCB1CTL1 |= UCSWRST;                       // Clear SW reset, resume operation
   UCB1CTL1 = UCSSEL_2 + UCSWRST;    // Use SMCLK, SW reset
  UCB1BR0 = baudrate;                          // set prescaler
  UCB1BR1 = 0;
  UCB1I2CSA = slave_address;                  // Set slave address
  UC1IFG &= ~(UCB1TXIFG+UCB1RXIFG);

  UCB1CTL1 &= ~UCSWRST;                       // Clear SW reset, resume operation
  if(UCB1STAT & UCBBUSY) I2C_BusReset();
}

unsigned char  I2C_ReadByteRs(unsigned char reg)
{
  UCB1CTL1 |= (UCTR + UCTXSTT);    // Master TX, start condition

  while (!(UC1IFG & UCB1TXIFG));      // Wait for address transmit
  UC1IFG &= ~UCB1TXIFG;
  UCB1TXBUF = reg;
  while (!(UC1IFG & UCB1TXIFG));      // Wait for register tx
  UC1IFG &= ~UCB1TXIFG;
  UCB1CTL1 &= ~UCTR;                    // Set master receiver mode
  UCB1CTL1 |= UCTXSTT;                  // Set start condition, Master rx
  while (UCB1CTL1 & UCTXSTT);         // Start condition sent?
  UCB1CTL1 |= UCTXSTP;                  // Set stop immediately
  while (!(UC1IFG & UCB1RXIFG));      // Wait for receive
   UC1IFG &= ~UCB1RXIFG;

  return UCB1RXBUF;
}


#pragma vector = USCIAB1RX_VECTOR
void USCIAB1RX_ISR(void)
{
   if (UCB1STAT & UCNACKIFG){                  // if slave sends NACK
    UCB1CTL1 |= UCTXSTP;                          // set STOP condition
    UCB1STAT &= ~UCNACKIFG;
    UC1IFG &= ~(UCB1TXIFG+UCB1RXIFG);
    // Здесь сообщить об ошибке
  }
}

И еще. Частая ошибка при работе с акселерометрами - забывают их инициализировать и сразу кидаются читать регистры осей. А многие акселерометры при включении уходят в сон и при чтении большинства регистров выдают нули.

 

2 rezident

Согласитесть, "TFM", который надо читать, в части I2C довольно мутный, точнее, слишком много впихнуто в крайне сжатом виде, рисунки - это вообще песня, учитывая, что легенда к ним приводится один раз и за 3 страницы до первого рисунка. Кстати, давно хотел спросить у Вас: учитывая Ваш опыт работы с MSP430, не кажется ли Вам, что I2C в 2хх намного глючнее, чем в 1хх?

Edited by =DS=

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this