|
Запись в Serial EEprom. |
|
|
|
Apr 11 2016, 13:01
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Записывая в память нужно следить чтобы не перейти страницу. Код #define USE_STATUS_REG 0 //doesn't help any way
void LOGGER_PageWrite(uint32_t address, uint32_t length, uint8_t *buffer) { #if USE_STATUS_REG uint32_t timer; #endif volatile uint32_t page_limit=0; volatile uint32_t bytes_to_fit=0; volatile uint32_t bytes_to_write=0;
//find a page limit for the address while (address > page_limit) { page_limit += 64; } //actual page limit page_limit--;
bytes_to_fit = page_limit - address; bytes_to_write = bytes_to_fit;
// Note!!!Every write operation demands a write enable!!! SPI_CSlow(); SPI_SendByte(SPIEEPROM_CMD_WREN); SPI_CShigh();
// start write SPI_CSlow(); // send write command SPI_SendByte(SPIEEPROM_CMD_WRITE); // send address SPI_SendByte(address>>8); SPI_SendByte(address&0x00FF); while(bytes_to_write--) { // send data to be written SPI_SendByte(*buffer++); } // stop write SPI_CShigh();
#if USE_STATUS_REG timer = LOGGER_DELAY; //wait for write operation to complete while(SPIEEPROM_ReadStatus() & SPIEEPROM_STATUS_WIP) { if(!--timer) break; } #else //needs 5ms for page write Delay_us_sys(5000); #endif
if (bytes_to_fit <= length) { address += bytes_to_fit+1; bytes_to_write = length - bytes_to_fit;
// Note!!!Every write operation demands a write enable!!! SPI_CSlow(); SPI_SendByte(SPIEEPROM_CMD_WREN); SPI_CShigh();
// start write SPI_CSlow(); // send write command SPI_SendByte(SPIEEPROM_CMD_WRITE); // send address SPI_SendByte(address>>8); SPI_SendByte(address&0xFF); while(bytes_to_write--) { // send data to be written SPI_SendByte(*buffer++); } // stop write SPI_CShigh();
#if USE_STATUS_REG timer = LOGGER_DELAY; //wait for write operation to complete while(SPIEEPROM_ReadStatus() & SPIEEPROM_STATUS_WIP) { if(!--timer) break; } #else //needs 5ms for page write Delay_us_sys(5000); #endif } } проверяю. пишу по середине большое количество байт чтоб перейти на другую страницу. Код uint8_t rxbuffer[50]; for (int i=0; i < 50; i++) { rxbuffer[i] = i+1; } LOGGER_PageWrite(80, 50, rxbuffer); и потом считываю. Код uint8_t txbuffer[50]; LOGGER_Read(80, 50,txbuffer); результаты такие: в адрессе 80-127 я вижу данные 1-47 и это правильно в адрессе 128-130 я вижу /0, 48, 49 то есть первый адресс следующей страницы не прописывается. целый день бьюсь над проблемой не могу понять в чем дело.
|
|
|
|
|
Apr 12 2016, 07:03
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
я извиняюсь немного непонятно это Код data += a; addr += a; len -= a; 'а' это bytes_to_write? переписал так Код void LOGGER_Write(uint32_t address, uint32_t lenght, uint8_t *data) { uint32_t timer; uint32_t bytes_to_write=0;
while(lenght) { if(((address & (PAGE_SIZE - 1)) + lenght) >= PAGE_SIZE) bytes_to_write = PAGE_SIZE - (address & (PAGE_SIZE - 1)); else bytes_to_write = lenght;
// Every write operation demands a write enable!!! SPI_CSlow(); SPI_SendByte(SPIEEPROM_CMD_WREN); SPI_CShigh();
// start write SPI_CSlow(); // send write command SPI_SendByte(SPIEEPROM_CMD_WRITE); // send address SPI_SendByte(address>>8); SPI_SendByte(address&0x00FF);
while(lenght--) { // send data to be written SPI_SendByte(*data++); } // stop write SPI_CShigh();
timer = LOGGER_DELAY; //wait for write operation to complete while(SPIEEPROM_ReadStatus() & SPIEEPROM_STATUS_WIP) { if(!--timer) break; } data += bytes_to_write; address += bytes_to_write; lenght -= bytes_to_write; } } заходит в функцию но на выходе попадает в эксепшен. Код Default_Handler: b Default_Handler
.size Default_Handler, .-Default_Handler
|
|
|
|
|
Apr 12 2016, 07:07
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(aaarrr @ Apr 12 2016, 02:26)  Какой-то ужас, если честно. Полностью с Вами согласен! Сочувствую работодателю топикстартера. И так чуть оптимальнее: Код void eep_write(uint32_t addr, uint32_t len, uint8_t *data) { uint32_t i; while (len) { i = EEPROM_PAGE_SIZE - (addr & EEPROM_PAGE_SIZE - 1); if (i > len) i = len; //тут проверяем статус EEPROM, посылаем WREN и пишем (addr, i, data); data += i; addr += i; len -= i; } } И между SPI_CShigh() и SPI_CSlow() которые между каждыми двумя соседними командами, нужна хотя-бы минимальная задержка, чтобы микросхема успела почувствовать CS high. А точнее - задержка в соответствии с даташитом на микросхему (CS high минимум time или что-то в этом роде).
|
|
|
|
|
Apr 12 2016, 07:09
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Jenya7 @ Apr 12 2016, 10:03)  я извиняюсь немного непонятно это Код data += a; addr += a; len -= a; 'а' это bytes_to_write? Да, забыл отредактировать. Цитата(Jenya7 @ Apr 12 2016, 10:03)  переписал так Должно быть так: Код void LOGGER_Write(uint32_t address, uint32_t lenght, uint8_t *data) { uint32_t timer; uint32_t bytes_to_write=0;
while(lenght) { if(((address & (PAGE_SIZE - 1)) + lenght) >= PAGE_SIZE) bytes_to_write = PAGE_SIZE - (address & (PAGE_SIZE - 1)); else bytes_to_write = lenght;
// Every write operation demands a write enable!!! SPI_CSlow(); SPI_SendByte(SPIEEPROM_CMD_WREN); SPI_CShigh();
// start write SPI_CSlow(); // send write command SPI_SendByte(SPIEEPROM_CMD_WRITE); // send address SPI_SendByte(address>>8); SPI_SendByte(address&0x00FF);
address += bytes_to_write; lenght -= bytes_to_write;
while(bytes_to_write--) { // send data to be written SPI_SendByte(*data++); } // stop write SPI_CShigh();
timer = LOGGER_DELAY; //wait for write operation to complete while(SPIEEPROM_ReadStatus() & SPIEEPROM_STATUS_WIP) { if(!--timer) break; } } }
|
|
|
|
|
Apr 12 2016, 08:05
|

Профессионал
    
Группа: Свой
Сообщений: 1 032
Регистрация: 13-03-08
Из: Маськва
Пользователь №: 35 877

|
Цитата Суд. Бракоразводный процесс. Судья спрашивает жену: - По какой причине вы хотите развестись с мужем? - Он меня как мужчина не удовлетворяет! Женский голос из зала: - Всех удовлетворяет, а её нет! Мужской голос из зала: - А её никто не удовлетворяет! Извините за флуд, но проверка регистра статуса - это рабочее решение (собственно, для того он там и есть). Ищите ошибку, кто такие LOGGER_DELAY, SPIEEPROM_ReadStatus() и SPIEEPROM_STATUS_WIP нам неведомо. И название микросхемы хорошо б сразу указывать. Тут, конечно, у многих навыки телепатии неплохие, но некрасиво это.
--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
|
|
|
|
|
Apr 12 2016, 08:16
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(aaarrr @ Apr 12 2016, 13:53)  Может, LOGGER_DELAY маловата? А зачем там вообще какая-то delay? Надо читать статус до тех пор пока не будет снят статус "занято". И делать это лучше не после, а перед операцией записи/стирания. Опять-же не выдерживается мин. время неактивности CS (ТС не читал даташит). Опять-же - не понятно как внутри устроена SPI_SendByte() у ТС, но очевидно что криво. Ибо, если она завершается сразу после записи байта данных в буферный регистр передатчика, то сразу после неё нельзя делать SPI_CShigh(); если же она завершается после опустошения сдвигового регистра передатчика, то это неоптимально (так как нет необходимости этого ждать внутри пакета байт команды).
|
|
|
|
|
Apr 12 2016, 08:27
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
таймаут нужен чтоб не застрять навсегда в цикле. у меня все циклы имееют таймаут. функции такие Код // commands #define SPIEEPROM_CMD_READ 0x03 // Read byte(s) #define SPIEEPROM_CMD_WRITE 0x02 // Write byte(s) #define SPIEEPROM_CMD_WREN 0x06 // Write Enable #define SPIEEPROM_CMD_WRDI 0x04 // Write Disable #define SPIEEPROM_CMD_RDSR 0x05 // Read Status Register #define SPIEEPROM_CMD_WRSR 0x01 // Write Status Register
// status register bit defines #define SPIEEPROM_STATUS_WIP 0x01 // Write in progress #define SPIEEPROM_STATUS_WEL 0x02 // Write enable #define SPIEEPROM_STATUS_BP0 0x04 // Block Protection 0 #define SPIEEPROM_STATUS_BP1 0x08 // Block Protection 1 #define SPIEEPROM_STATUS_WPEN 0x80 // Write Protect Enable
uint32_t SPI_TransferByte(uint32_t sendbyte) { uint32_t timeout = SPI_DELAY; /* Check that transmit buffer is empty */ while (!(SPI_USART->STATUS & USART_STATUS_TXBL)) { if(!--timeout) break; } SPI_USART->TXDATA = (uint32_t)sendbyte; /* Wait for transmitting to finished */ timeout = SPI_DELAY; while (!(SPI_USART->STATUS & USART_STATUS_TXC)) { if(!--timeout) break; } //return data; return majority32const(&SPI_USART->RXDATA); }
void SPI_SendByte(uint8_t sendbyte) { uint32_t timeout = SPI_DELAY;
USART_Tx(SPI_USART,sendbyte);
/* Wait for transmitting to finished */ while (!(SPI_USART->STATUS & USART_STATUS_TXC)) { if(!--timeout) break; } }
uint8_t SPIEEPROM_ReadStatus(void) { uint8_t status; SPI_CSlow(); // send read status register command SPI_SendByte(SPIEEPROM_CMD_RDSR); // get status register value status = SPI_TransferByte(0xFF); SPI_CShigh(); return status; } память CAT25256
Сообщение отредактировал Jenya7 - Apr 12 2016, 08:33
|
|
|
|
|
Apr 12 2016, 08:59
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(aaarrr @ Apr 12 2016, 14:26)  Выход по тайм-ауту в таких случаях желательно все же иметь. Но при аварии надо ошибку вернуть, конечно, а не просто вывалиться. Да, а потом ещё не забыть сделать обработку этих возвращаемых статусов ошибок во всех точках вызова (что обычно обязательно забывают где-нить сделать). Или вернуть этот статус выше и там обработать. В любом случае с этой ошибкой (таймаут неготовности) надо что-то делать. А что делать? Ведь если превышен таймаут (который выставлен правильно в соответствии с даташитом), то значит микросхема не функционирует. Вы все характеристики всех элементов в схеме проверяете на их соответствие заявленным в даташите, или всё же предполагаете, что она должна функционировать как обещал производитель? Я обычно тоже делаю контроль таймаута в таких случаях, но при возникновении события таймаута (превышения времени заявленного в даташите) не возвращаю какой-то статус, а вызываю событие "критическая ошибка" (так как это явно отказ аппаратуры). Обработчик такого события или просто выполняет аппаратный сброс или например входит в состояние "критическая ошибка" с индикацией этого состояния (в режиме отладки). Цитата(Jenya7 @ Apr 12 2016, 14:27)  таймаут нужен чтоб не застрять навсегда в цикле. у меня все циклы имееют таймаут. Хорошо, вот наступил этот таймаут. Ваши действия? Что после этого делать-то?
|
|
|
|
|
Apr 12 2016, 09:08
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Цитата(jcxz @ Apr 12 2016, 14:59)  Да, а потом ещё не забыть сделать обработку этих возвращаемых статусов ошибок во всех точках вызова (что обычно обязательно забывают где-нить сделать). Или вернуть этот статус выше и там обработать. В любом случае с этой ошибкой (таймаут неготовности) надо что-то делать. А что делать? Ведь если превышен таймаут (который выставлен правильно в соответствии с даташитом), то значит микросхема не функционирует. Вы все характеристики всех элементов в схеме проверяете на их соответствие заявленным в даташите, или всё же предполагаете, что она должна функционировать как обещал производитель? Я обычно тоже делаю контроль таймаута в таких случаях, но при возникновении события таймаута (превышения времени заявленного в даташите) не возвращаю какой-то статус, а вызываю событие "критическая ошибка" (так как это явно отказ аппаратуры). Обработчик такого события или просто выполняет аппаратный сброс или например входит в состояние "критическая ошибка" с индикацией этого состояния (в режиме отладки).
Хорошо, вот наступил этот таймаут. Ваши действия? Что после этого делать-то? это уже следующий этап. сейчас мне важно чтоб система не зависла. в принцине у меня есть переменная которая содержит флаги ошибок которые я периодически тестирю в коде. Цитата(jcxz @ Apr 12 2016, 15:03)  А как такое может быть - "обращения к SPI при выключенном клоке"??? Ведь этим клоком должен управлять этот-же код, который управляет обменом по SPI. Значит он сам его и выключил? это маловероятно - но на этот случай есть таймаут.
|
|
|
|
|
Apr 12 2016, 09:16
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Jenya7 @ Apr 12 2016, 15:05)  это уже следующий этап. сейчас мне важно чтоб система не зависла. в принцине у меня есть переменная которая содержит флаги ошибок которые я периодически тестирю в коде. Так а как Вы вообще отлаживаете? Что значит "не важно"? Вот у Вас скажем одно из обращений к EEPROM закончилось таймаутом, выставился где-то какой-то флаг и поехало дальше выполнение. Дальше опять вызывается транзакция с этой EEPROM, а у Вас при старте транзакции не проверятся что предыдущая завершилась (а предыдущая завершилась таймаутом и EEPROM всё ещё busy) и Вы начинаете гнать туда какие-то байты, считая что идёт запись. В это время EEPROM по середине этих данных наконец снимает статус busy и воспринимает очередной байт данных как некую команду (например стирания или записи или перевода в сон и т.п.). И что будет после этого в Вашем ПО и EEPROM? Каша! Вот именно поэтому и могут происходить такие проблемы как Вы писали в первом посте. Ну а если вообще также халтурно подходить к написанию ПО, то вообще где угодно могут быть проблемы. Цитата(Jenya7 @ Apr 12 2016, 15:08)  это маловероятно - но на этот случай есть таймаут. Пипец просто! Вы читаете что Вам пишут? Ваш таймаут - это костыль на Ваши же баги. Таймаут вообще не для этого нужен. Он нужен чтобы отследить сбои в работе вызванные внешними причинами (неисправностью внешней микросхемы), а не проблемами в Вашем коде.
|
|
|
|
|
Apr 12 2016, 09:21
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Цитата(jcxz @ Apr 12 2016, 15:16)  Так а как Вы вообще отлаживаете? Что значит "не важно"? Вот у Вас скажем одно из обращений к EEPROM закончилось таймаутом, выставился где-то какой-то флаг и поехало дальше выполнение. Дальше опять вызывается транзакция с этой EEPROM, а у Вас при старте транзакции не проверятся что предыдущая завершилась (а предыдущая завершилась таймаутом и EEPROM всё ещё busy) и Вы начинаете гнать туда какие-то байты, считая что идёт запись. В это время EEPROM по середине этих данных наконец снимает статус busy и воспринимает очередной байт данных как некую команду (например стирания или записи или перевода в сон и т.п.). И что будет после этого в Вашем ПО и EEPROM? Каша!
Вот именно поэтому и могут происходить такие проблемы как Вы писали в первом посте. Ну а если вообще также халтурно подходить к написанию ПО, то вообще где угодно могут быть проблемы.
Пипец просто! Вы читаете что Вам пишут? Ваш таймаут - это костыль на Ваши же баги. Таймаут вообще не для этого нужен. Он нужен чтобы отследить сбои в работе вызванные внешними причинами (неисправностью внешней микросхемы), а не проблемами в Вашем коде. Вы знаете мне бы хотелось для начала отладить запись в EEPROM а потом уже решать остальные проблемы. и если рассуждать логически - если заменив это Код while(SPIEEPROM_ReadStatus() & SPIEEPROM_STATUS_WIP) { //NO TIMEOUT } на это Код Delay_us(5000); все успевает прописаться то очевидно предыдущий код не содержит смертных грехов.
|
|
|
|
|
Apr 12 2016, 10:02
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Цитата(aaarrr @ Apr 12 2016, 15:50)  Взял первую попавшуюся цифру для SPI - 3uA/MHz. Но не надо забывать, что это цифра для работающей периферии, а не простаивающей с включенным клоком. Реальное значение лучше замерить, и сомневаюсь, что Вы найдете сколько-нибудь значительную разницу. моя система потребляет 6 микро в ожидающем режиме. много-мало это беспредметный спор - все зависит от требований. вот почему статус бит не тестируется - вот это вопрос. желательно разрешить проблему пока предупреждения не досигли 100%. Товарищ Herz не дремлет, спасибо ему.  в даташит написано Код tWC (Note 13) Write Cycle Time 5 ms .
13.tWC is the time from the rising edge of CS after a valid write sequence to the end of the internal write cycle. получается как ни крути а 5мс нужно ждать?
Сообщение отредактировал Jenya7 - Apr 12 2016, 12:48
|
|
|
|
|
Apr 12 2016, 13:10
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Jenya7 @ Apr 12 2016, 13:02)  моя система потребляет 6 микро в ожидающем режиме. Верю. Но тут вопрос сколько она потребляет в активном режиме, и сколько на этом фоне занимает SPI. Цитата(Jenya7 @ Apr 12 2016, 13:02)  получается как ни крути а 5мс нужно ждать? Почему? 5мс - максимальное время записи, опрашивать статусный регистр можно и нужно.
|
|
|
|
|
Apr 12 2016, 13:36
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
чобы было легче дебагировать я немного переделал опрос регистра статуса. Код busy = 1; //wait for write operation to complete while(busy) { busy = (SPIEEPROM_ReadStatus() & SPIEEPROM_STATUS_WIP); } когда то бит обнуляется. в режиме отладчика реальное время трудно замерять. но подозреваю что бит не отражает реальное положение вещей.
|
|
|
|
|
Apr 12 2016, 13:53
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Jenya7 @ Apr 12 2016, 16:02)  получается как ни крути а 5мс нужно ждать? Нет. Нужно правильно написать чтение статуса. Цитата(Jenya7 @ Apr 12 2016, 19:36)  когда то бит обнуляется. в режиме отладчика реальное время трудно замерять. но подозреваю что бит не отражает реальное положение вещей. Отражает. Ищите баги у себя.
|
|
|
|
|
Apr 12 2016, 14:18
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
ввел задержку. Код busy = 1; //wait for write operation to complete while(busy) { busy = (SPIEEPROM_ReadStatus() & SPIEEPROM_STATUS_WIP); Delay_us(1); } теперь все прописывается корректно. очевидно нога слейв селект не успевала отработать в цикле. спасибо за помощь. что интересно - во всех примерах статус регистр опрашивается в цикле без задержки. или это я такой особенный или ребята выдают код с багами. вобщем я оптимизировал чтение статус регистра. функцию чтения нужно переписать так Код uint8_t SPIEEPROM_ReadStatus(void) { uint8_t status; //SPI_CSlow(); //outside
// send read status register command //SPI_SendByte(SPIEEPROM_CMD_RDSR); //transfer - not send SPI_TransferByte(SPIEEPROM_CMD_RDSR);
// get status register value status = SPI_TransferByte(0xFF);
//SPI_CShigh(); //outside
return status; } а управление слейв селект вынести вне цикла. Код busy = 1; //wait for write operation to complete SPI_CSlow(); while(busy) { busy = (SPIEEPROM_ReadStatus() & SPIEEPROM_STATUS_WIP); } SPI_CShigh();
Сообщение отредактировал Jenya7 - Apr 12 2016, 14:47
|
|
|
|
|
Apr 13 2016, 05:46
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Jenya7 @ Apr 12 2016, 20:18)  ввел задержку. Delay_us(1); теперь все прописывается корректно. очевидно нога слейв селект не успевала отработать в цикле. Я Вам несколько раз писал, что у Вас между CShigh и следующим CSlow нет никакого интервала времени. Цитата(Jenya7 @ Apr 12 2016, 20:18)  а управление слейв селект вынести вне цикла. ... Лучше каждую новую команду к EEPROM начинать с нового спада импульса на CS. Каждый спад CS должен сбрасывать автомат состояний внутри микросхемы (перезапускать битовые и прочие счётчики). Бит занятости в слове статуса - 0-й бит? Если да - можно ещё попробовать просто выдать команду чтения статуса и дальше просто держать CS и смотреть на состояние линии MISO: некоторые микросхемы SPI-flash, в которых в статусном регистре бит занятости 0й, могут выдавать в этом случае на линию MISO текущее состояние бита. Т.е. - просто ждёте пока линия не перейдёт в состояние свободно - операция записи закончилась. Если Ваша EEPROM так умеет, тогда не нужно периодически выдавать команду чтения статуса - достаточно её передать один раз.
|
|
|
|
|
Apr 13 2016, 06:50
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Цитата(jcxz @ Apr 13 2016, 12:33)  Откройте диаграмму чтения слова состояния в даташите. Там обычно рисуют что после последнего клока остаётся состояние последнего выдвинутого бита сколько угодно долго. Вопрос только в том - выводится ли там текущее состояние бита занятости или защёлкнутое в момент перепада SCLK. там написано так. Цитата To read the status register, the host simply sends a RDSR command. After receiving the last bit of the command, the CAT25256 will shift out the contents of the status register on the SO pin (Figure 10). The status register may be read at any time, including during an internal write cycle. While the internal write cycle is in progress, the RDSR command will output the full content of the status register (New product, Rev. E) or the RDY (Ready) bit only (i.e., data out = FFh) for previous product revisions C, D (Mature product). For easy detection of the internal write cycle completion, both during writing to the memory array and to the status register, we recommend sampling the RDY bit only through the polling routine. After detecting the RDY bit “0”, the next RDSR instruction will always output the expected content of the status register. они рекомендуют поллинг то есть обращение к регистру в цикле. непонятно только что делать с ногой слейв селект.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|