Jump to content

    

Освоение MSP430 / первые шаги

Ещё такое соображение. Есть две команды

UCB1CTLW1 = UCASTP_2;                     // automatic stop assertion
UCB1TBCNT = 1;                            // TX only 1 byte of data

Это счётчик автогенерации сигнала STOP, а значение UCASTP_2 разрешает его работу. Т.е. в данной программе сигнал STOP вырабатывается автоматически (скорее всего).

В документации на модуль I2C этого контроллера пишут, что при использовании авто-STOP только с одним байтом, не надо устанавливать STOP из программы. Документ slau445i (стр.637)

Цитата

When UCASTPx = 10 is set, the byte counter is used for STOP generation and the user does not need to set the UCTXSTP. This is recommended when transmitting only one byte.

Почему именно для одного байта, хз. Но это даже выделено в тексте...

 

Может установка STOP в обработчике приёма байта, что-то там сбивает в этом механизме? Закомментируйте её для пробы (и заодно узнаем, работает ли авто-STOP).

Или закомментить саму инициализацию авто-STOP. Посмотреть что получится.

Share this post


Link to post
Share on other sites

Спасибо, вроде дошло. Я думал, что правильно - подавать команду на формирование STOP после чтения приёмного буфера, чтобы приём не прервался. В итоге эта команда проходила с опозданием, уже во время следующей записи, прерывая её сразу после отправки адреса. Оказалось, что скомандовать "отправить STOP" можно (и нужно) уже после того, как прошёл START (то есть сбросился флаг UCTXSTT). Тогда приёмник выполнит всё, что требуется: и адрес слейва передаст, и ответ в буфер примет.

51 minutes ago, controller_m30 said:

Это счётчик автогенерации сигнала STOP, а значение UCASTP_2 разрешает его работу. Т.е. в данной программе сигнал STOP вырабатывается автоматически (скорее всего).

В документации на модуль I2C этого контроллера пишут, что при использовании авто-STOP только с одним байтом, не надо устанавливать STOP из программы. Документ slau445i (стр.637)

Почему именно для одного байта, хз. Но это даже выделено в тексте...

Автоматически STOP вырабатывается только в режиме записи, у меня так и происходит. Не пробовал пока, будет ли работать с более чем одним байтом данных. Должен, по идее, на то и счётчик передаваемых байтов UCB1TBCNT. При чтении же нужно STOP посылать самому. Хотя, проверить ещё надо. Однако, если на запись счётчик передаваемых байтов есть, то такого на принимаемые байты нет, поэтому приёмник и не может автоматом генерировать STOP, как я понимаю.

18 hours ago, Сергей Борщ said:

И еще один момент: после получения последнего байта ведущий должен выставить на шину NAK вместо ACK. Это заставит ведомого прекратить выдавать биты на шину и позволит ведущему сформировать STOP или повторный START после окончания NACK (в противном случае ведомый начнет в этом месте выдавать первый бит следующего байта, который может оказаться нулем и не позволит сформировать STOP или START). При беглом просмотре вашего кода из первого сообщения никаких упоминаний NACK я не увидел.

Спасибо, хорошее замечание. Но сейчас у меня NACK и так автоматически отсылается пред СТОП-ом  в режиме чтения. Возможно, как раз потому, что я принудительно отправляю команду STOP до чтения буфера.

Quote

A STOP condition is either generated by the automatic STOP generation or by setting the UCTXSTP bit.
The next byte received from the slave is followed by a NACK and a STOP condition. This NACK occurs
immediately if the eUSCI_B module is currently waiting for UCBxRXBUF to be read.

20200514-0001.thumb.jpg.57cc00ff9c8c17a03f14f50b435ff6db.jpg

Share this post


Link to post
Share on other sites

Ещё парадокс. Модуль eUSCI_B у меня тактируется от SMCLK, коэффициент деления в регистре UCB1BRW выставлен 8. То есть, частота BRCLK (она же SMCLK) должна быть, по идее, в восемь раз выше, чем SCL. Clock System Block настроен на использование DCO, его частота (по умолчанию) - 8MHz.

Pdf1.thumb.jpg.b549149e6f91864f5158bb05f9e33bb0.jpg

Биты DIVM и DIVS в регистре SLCTL5 установлены в 0, то есть коэффициент деления = 1. Однако, при этих условиях частоту SCL на шине I2C я получаю равной 132кГц.

Больше того, если я меняю частоту DCO (больше ничего) от 1 до 24MHz, то выходит такая зависимость:

Excel1.thumb.jpg.ecf9c6f5230f58e7927565fa29017d94.jpg

Здесь в первой колонке - частота DCO, во второй - измеренная SCL, в третьей - рассчитанная SMCLK (SCL x8), а в четвёртой - коэффициент деления основной частоты (DCO), чтобы получить такую SMCLK (условно DIVM, который должен быть =1, или хотя бы константой). Странно это.

Share this post


Link to post
Share on other sites

Мне кажется, в предыдущих моделях, DCO было настроено по умолчанию на 1MHz (или на 2MHz). Может и в этой традицию сохранили.

Если по умолчанию 1MHz, то после деления на 8 как раз получается что-то близкое к 132KHz. Надо это как-то проверить. 

 

Для удобства, в некоторых моделях можно вывести сигналы MCLK, SMCLK, ACLK прямо на пины ввода-вывода (это на распиновке микросхемы есть, среди кучи остальных функций)

В этой модели, MCLK можно вывести на P2.6; P3.0.

Какие настройки пинов сделать чтоб вывести MCLK, приведено в документе на стр.98 (для P2.6), и стр.100 (для P3.0).

Спойлер

Регистр PxSEL указанный в таблицах, на самом деле состоит из двух регистров, PxSEL0 и PxSEL1. В первом содержится младший бит, а во втором старший бит. Вместе они образуют двухбитовое значение альтернативной функции для каждого пина. Подробно про это на стр.314 документ slau445i

 

И для остальных сигналов:

SMCLK - P1.0; P3.4

ACLK - P1.1

Edited by controller_m30

Share this post


Link to post
Share on other sites
18 часов назад, Herz сказал:

Clock System Block настроен на использование DCO, его частота (по умолчанию) - 8MHz. 

Биты DIVM и DIVS в регистре SLCTL5 установлены в 0, то есть коэффициент деления = 1. Однако, при этих условиях частоту SCL на шине I2C я получаю равной 132кГц.

Почитал описание системы тактирования на MSP430FR2355 - мозг начал сворачиваться в трубочку :crazy:
Написано так, что и 100 граммов будет мало.

Написано следующее:
After PUC, DCO locked by FLL operation with XT1CLK is selected by default. The FLL stabilizes MCLK and SMCLK to 1 MHz and fDCOCLKDIV = 1 MHz
однако дальше:
By default, the crystal pins (XT1IN, XT1OUT) are shared with general-purpose I/Os. To enable XT1, the PSEL bits
И каким образом MCLK будет 1 MHz, если DCO по умолчанию управляется FLL, которая должна тактироваться от XT1, но который по умолчанию отключен?
Читаем дальше:
If XT1 is used but does not work properly, fault protection logic forces REFO as the FLL reference clock.
Т.е. происходит следующее (ИМХО, что там на самом деле, не знаю :smile: ):
Кварца нет, да и он после рестарта выключен. Тактирование переключается на REFO = 32768 Hz
Дальше на FLL c параметрами FLLD = 1 (/2) и FLLN = 31 (х32). Получаем fDCOCLK = 32.768 х 32 х 2 = 2.097152 MHz
fDCOCLKDIV = 1.048576 MHz
Потом делите на 8 и получаете 131 кГц

Ну а когда начинаете менять частоту DCO, тут вааще непонятно что происходит и как это у них работает, т.к. пока DCO контролируется FLL, и чтобы переключить его на ручное управление, нужно:
By default, FLL operation is enabled. FLL operation can be disabled by setting SCG0 or SCG1. When the FLL is disabled, the DCO continues to operate at the current settings defined in CSCTL0 and CSCTL1. The DCO frequency can be adjusted manually if desired. Otherwise, the DCO frequency is stabilized by the FLL operation.
С непривычки просто мрак :dash1:
Пойду я лучше в CubeMX галочек понаставлю и буду на диване валяться...

Share this post


Link to post
Share on other sites
26 minutes ago, Baser said:

Ну а когда начинаете менять частоту DCO, тут вааще непонятно что происходит и как это у них работает, т.к. пока DCO контролируется FLL, и чтобы переключить его на ручное управление, нужно:
By default, FLL operation is enabled. FLL operation can be disabled by setting SCG0 or SCG1. When the FLL is disabled, the DCO continues to operate at the current settings defined in CSCTL0 and CSCTL1. The DCO frequency can be adjusted manually if desired. Otherwise, the DCO frequency is stabilized by the FLL operation.
 С непривычки просто мрак :dash1:
 Поиду я лучше в CubeMX галочек понаставлю и пойду на диване валяться...

раньше (в F1,F2,G2,...) DCO был вообще без FLL, и его частота просто определялась через CSCTL0 and CSCTL1, а теперь при включенном FLL он опирается либо на кварц либо на RC, и соответственно для изменения частоты менять надо множители FLL, а он уже будет подстраивать DCO. параллельно с FLL руками туда лазить не надо.

для смены частоты надо крутить множитель FLLN в CSCTL2, а Herz насколько я понял менял DCORSEL, который не совсем меняет частоту, а задаёт диапазон (DCORSEL=24МГц лишь значит что частота может быть в диапазоне от 12 до 45МГц :)), Figure 5-6. Typical DCO Frequency из даташита, но только когда FLL отключен.

соответственно FLL от такого обращения (когда кто-то его генератору параллельно режимы меняет пока он пытается частоту удержать) скорее всего дурел и какую попало частоту выдавал.

 

Share this post


Link to post
Share on other sites

Да, похоже, я недооценил сложность процедуры смены частоты, хоть в документации так бодро заявлено:

Quote

The CS module can be configured or reconfigured by software at any time during program execution.

Quote

The DCO frequency can be adjusted by software using the DCORSEL, DCO, and MOD bits.

Придётся покопать глубже.

Share this post


Link to post
Share on other sites

Так исходники CS_initFLLSettle вроде как не секрет...

Share this post


Link to post
Share on other sites
9 minutes ago, Herz said:

Да, похоже, я недооценил сложность процедуры смены частоты, хоть в документации так бодро заявлено:

Придётся покопать глубже.

Всё правильно заявлено, однако CS это не только DCO, который есть просто управляемый генератор, а ещё и FLL, который может этот генератор под любой другой более стабильный источник частоты (внутренний RC, кварц) подстраивать.

То есть для DCO надо выставить диапазон через DCORSEL, а у FLL задать множители/делители. И он соответственно регистры MOD and DCO сам подстроит для совпадения с умноженной опорной частотой. А если поменять только рабочий диапазон DCORSEL, а FLL при этом по прежнему заставлять держать частоту 1МГц по умолчанию, то ничего хорошего не получится.

можно FLL выключить и DCO руками покрутить, но его стабильность будет хуже чем у внутреннего 32кГц RC, частоту которого FLL по умолчанию и умножает.

Share this post


Link to post
Share on other sites

И снова странность, которой, вроде, никак быть не может. Работа eUSCI_B0 в режиме SPI, попытка обращения к SD-карточке.

Частота DCO, опять же, по умолчанию. И выходит, что действительно ок. 1МГц. Сам SPI работает, как положено, SCLK порядка четверти мегагерца.

Вот кусок кода, в котором специально выброшено всё лишнее. Карточки ещё нет, вход-выход соединены перемычкой. Порт Р3.0 будет использоваться как CS.

Передаётся (и, соответственно, принимается) непрерывная последовательность из шести байт.
 

//******************************************************************************
//   Description: This program demonstrate USCI_B0 in SPI mode interfaced to a
//   SD-card. P1.0 indicate STE signal.
//    MCLK = SMCLK = DCO ~1MHz, BRCLK = SMCLK/4 = 264kHz
//
//
//                         MSP430FR2355
//                       -----------------
//                   /|\|              XIN|-
//       SD           | |                 |
//    -------------   --|RST          XOUT|-
//   |           CS|<---|P3.0             |
//   |      DATAOUT|--->|P1.3/UCB0SOMI    |
//   |      DATAIN |<---|P1.2/UCB0MOSI    |
//   |      I/O CLK|<---|P1.1/UCB0CLK     |
//   |             |    |             P1.0|--> LED/STE
//
//
//******************************************************************************

#include <msp430.h>

#define CS_HIGH     P3OUT |= 0x01
#define CS_LOW      P3OUT &= ~0x01

/**
 * main.c
 */
int main(void)
{
 unsigned int data1, data2, i;

      WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
// Disable the GPIO power-on default high-impedance mode
// to activate previously configured port settings
      PM5CTL0 &= ~LOCKLPM5;
// eUSCI_B0 Initializing
      UCB0CTL1 |= UCSWRST;                      // Enable SW reset
      P1DIR |= BIT0;                            // Set P1.0 to output direction
      P1SEL0 |= BIT0 + BIT1 + BIT2 + BIT3;      // P1.0 - P1.3 USCI_B0 option select
      P3DIR |= BIT0;                            // P3.0 output direction (CS)
      UCB0CTLW0 |= UCMSB + UCMST + UCSYNC;      // 8-bit SPI mstr, MSB 1st, sync
      UCB0CTLW0 |= UCMODE1 + UCSSEL1 + UCSTEM;  // 4-pin mode, STE low, SMCLK
      UCB0BRW = 4;                              // division, clock eq. to BRCLK/4
      UCB0STATW = 0;                            // clear errors
      UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation

/*
// initialize SD-card in SPI-mode and reset
      CS_HIGH;
      for (i=0; i<10; i++)                      // for more than 74 cycles
      {
         UCB0TXBUF = 0;                         // send clock pulses at CS high state
         while (!(UCB0IFG & UCTXIFG));
      }
*/
 // SD soft reset sequence
      CS_LOW;
      UCB0TXBUF = 0x40;
      while (!(UCB0IFG & UCTXIFG));
      UCB0TXBUF = 0;
      while (!(UCB0IFG & UCTXIFG));
      UCB0TXBUF = 0;
      while (!(UCB0IFG & UCTXIFG));
      UCB0TXBUF = 0;
      while (!(UCB0IFG & UCTXIFG));
      UCB0TXBUF = 0;
      while (!(UCB0IFG & UCTXIFG));
      UCB0TXBUF = 0x95;
      while (!(UCB0IFG & UCRXIFG));           // USCI_B0 RX buffer ready?
      data2 = UCB0RXBUF;
      CS_HIGH;

И всё бы хорошо, но почему-то уровень на CS возвращается в лог.1 только через 60 мсек! На картинке - красный луч. Зелёный - данные, синий - клок.

20200529-0002.thumb.gif.cd3c3d213f9c0b86242c4e647bb7402a.gif

Такое впечатление, что МК на это время просто засыпает...

Share this post


Link to post
Share on other sites

вы используете 4 проводный режим SPI мастера, но при этом чипселект (причем другой, а не аппаратный к USCI привязанный) сами пытаетесь руками дергать.

уберите UCMODE1.

и у мастера вроде как два выхода должно быть клоки и mosi, а не один: P1DIR |= BIT0;

Share this post


Link to post
Share on other sites

Да, я использую 4-проводный режим SPI, просто для проверки работы сигнала STE. На поведение порта Р3.0 это никак не сказывается, да и не должно. Ещё, несмотря на то, что только один пин порта 1 указан явно как выход ( P1DIR |= BIT0;), оба сигнала (MOSI и SCLK) формируются нормально, это не проблема. Я думаю, так и должно быть: когда мы определяем выводы порта, работающие с периферийным модулем  eUSCI, уже неважно, как они настроены в смысле порта ввода-вывода - выходы или входы... 

То есть, странность именно в запаздывании реакции на команду

P3OUT |= 0x01;

что, как бы, никакого отношения к SPI не имеет. Как видно по осциллограмме, коммуникация давным-давно закончилась... Ума не приложу, что за задержка.

Share this post


Link to post
Share on other sites

Кстати, вот такой малоотличимый от вышеприведенного код выполняется вполне корректно:

//******************************************************************************

#include <msp430.h> 


/**
 * main.c
 */
int main(void)
{
	  unsigned int data1, data2;

	  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
// Disable the GPIO power-on default high-impedance mode
// to activate previously configured port settings
	  PM5CTL0 &= ~LOCKLPM5;
// eUSCI_B0 Initializing
      UCB0CTL1 |= UCSWRST;                      // Enable SW reset
	  P1DIR |= BIT0;                            // Set P1.0 to output direction
	  P1SEL0 |= BIT0 + BIT1 + BIT2 + BIT3;      // P1.0 - P1.3 USCI_B0 option select
	  P3DIR |= BIT0;                            // P3.0 output direction (CS)
	  UCB0CTLW0 |= UCMSB + UCMST + UCSYNC;      // 8-bit SPI mstr, MSB 1st, sync
	  UCB0CTLW0 |= UCMODE1 + UCSSEL1 + UCSTEM;  // 4-pin mode, STE low, SMCLK
	  UCB0BRW = 4;                              // division, clock eq. to BRCLK/4
	  UCB0STATW = 0;                            // clear errors
	  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation

	  data1 = 0xAA;

	  while(1)
	  {
	    P1OUT &= ~0x01;                         // P1.0 = 0 (LED off)
	    P3OUT &= ~0x01;                         // Enable slave, /CS - low
	    UCB0TXBUF = data1;                      // Write to transmitter to start SPI

	    while (!(UCB0IFG & UCRXIFG));           // USCI_B0 RX buffer ready?
	    data2 = UCB0RXBUF;                      //

	    P3OUT |= 0x01;                          // Disable slave, /CS set


	    if (data1 == data2)                     // Test for correct character RX'd
	      P1OUT |= 0x01;                        // P1.0 = 1 (LED on)
	  }


	
	return 0;
}

Здесь на осциллограмме ниже: синий и красный лучи, как и прежде, SCLK и P3.0 соответственно; зелёный - аппаратный STE.

Как видно, использование аппаратного чипселекта выгоднее, можно сделать меньшими паузы между посылками. Что естественно. (У меня такой возможности нет, поскольку инициализация SD-карты предполагает подачу, как минимум, 74 периода SCLK при высоком уровне CS).

Но ни о каких миллисекундах тут и речи нет... Длительность низкого уровня на P3.0 - 48 мкск.

20200529-0004_01.thumb.gif.c1cd117bc735a8c0171da8c3161a3d0f.gif

 

Share this post


Link to post
Share on other sites

Попробуйте такие варианты:

1. Перед записью байта 0х95 сделайте упреждающее чтение регистра UCBxRXBUF, чтобы очистить флаг UCRXIFG, который установлен прошлыми принятыми байтами.

 

2. Замените все команды 

while (!(UCB0IFG & UCTXIFG));

т.е. проверку опустошения буфера записи

на проверку заполнения буфера чтения:

while (!(UCB0IFG & UCRXIFG));

с обязательным считыванием регистра UCBxRXBUF для того, чтобы флаг UCRXIFG сбрасывался.

Идея в том, что UCTXIFG=1 уже в начале передачи данных, а UCRXIFG=1 только в конце SPI-транзакции. Замена команд нужна для того, чтобы гарантированно привязаться к моменту завершения каждой SPI-транзакции. Может это на что-то повлияет.

 

3. Вставьте команды 

CS_LOW; CS_HIGH;

между каждым передаваемым байтом, чтоб посмотреть на время реакции процессора при передаче всех предыдущих данных. Происходит ли задержка 60 мсек каждый раз, или только в конце пересылки группы байт?

Share this post


Link to post
Share on other sites

Спасибо за советы. К сожалению, ничего из этого ситуацию не изменило и прояснить её не помогло. Замена проверки буфера передатчика на проверку буфера приёмника (конечно, со сбросом соответствующего флага) ни на что не повлияло. Чуть растянуло, разумеется, коммуникацию. Манипуляция сигналом CS в промежутках между пересылкой байтов тоже отрабатывается корректно, без жуткой задержки. Происходит она, как и прежде, только в конце.

Тут, очевидно, что-то компилятор наоптимизировал. Сначала я обнаружил, что он переменные data1 и data2 вообще считал (когда ему было удобно) одной и той же ячейкой памяти, пока я не объявил их volatile. Ну, имел право, раз я данными не пользовался...

И вот ещё один интересный момент. Если я заканчиваю транзакцию так, как показал выше, то есть:

      while (!(UCB0IFG & UCRXIFG));           // USCI_B0 RX buffer ready?
      data2 = UCB0RXBUF;
      CS_HIGH;

то дикая задержка в 60 мс наличествует. Если же меняю местами чтение буфера с установкой CS в 1:

    while (!(UCB0IFG & UCRXIFG));           // USCI_B0 RX buffer ready?
 	CS_HIGH;
	data2 = UCB0RXBUF;

то она исчезает. После последнего импульса клока до поднятия ноги P3.0 проходит 10 мкс. Можно было бы предположить, что именно чтение буфера происходит, почему-то, так долго.

Но если я вообще убираю чтение буфера:

      while (!(UCB0IFG & UCRXIFG));
      CS_HIGH;
//      data2 = UCB0RXBUF;

то задержка чудесным образом возвращается!

Однако, дизассемблер ничего лишнего не указывает.

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