worklog: I2C в STM32F030 - почти победил (2)
Короче, я как старый солдат, не знающий слов любви, сначала решил обойтись наипростейшей реализацией протокола, о чём я уже писал. И для однобайтовых посылок он работает вполне-вполне. Но если нам нужно больше, чем 1 байт полезной нагрузки, то тут возможен сюрприз в виде "код без прерываний неработоспособен", как-то так.
Суть проблемы: после того, как мы получили и распознали адрес от ведущего устройства, мы должны (будучи в режиме slave transmitter) выложить наши данные в буфер. Предварительно убедившись в том, что буфер в самом деле пуст (для чего есть ажно два флага с очень схожим, прям до почти полного отсутствия различий, смыслом -- I2C_ISR_TXE и I2C_ISR_TXIS), кладём байтик в I2C1->TXDR и ждём взведения того же бита, что мы только что проверяли - как только он станет равен 1, это означает, что байт передан. Но! ...
И вот тут эта бомбачка как раз и лежит.
Взведение флага "байт передан" вовсе не означает, что мы получили ACK/NACK бит от принимающей стороны! Нихера подобного! Вообще всё не так! Флаг взводится (или нет) ПОТОМ, когда реальный бит реально прошёл по реальным проводам и перещёлкнул логику внутри I2C модуля. А это довольно-таки длительный промежуток времени по меркам 48-мегагерцового процессора. Задача осложняется тем, что ACK и NACK - это просто два разных значения одного и того же бита, и мы не можем никак но узнать в общем случае, пришёл ли он вообще. Да, можно, например, дождаться взведения флага I2C_ISR_NACKF - и это работает... только в том случае, если в самом деле пришёл именно NACK (потому что он равен 1). Но флаг не будет взведён, если пришёл бит ACK (ибо он равен 0) - значение флага останется нулевым и мы будем ждать его изменений вечно.
Без использования прерываний эту проблему почти никак не решить (топорные методы а-ля "поставить задержку и посмотреть заново состояние флагов" не предлагать). Причём обработчик должен ловить и событие "буфер передатчика опустошён", и "получен NACK", и "получен STOP"... эхохо.
Ладно. Я заранее знал, что совсем простой код не годится для чуточку более сложной задачи. Но всё равно как-то странно - что мешало проектировщикам железа добавить флаг ACK в тот регистр, не понимаю...
Upd Переписал на работу с прерываниями и наткнулся на очередной сюрприз. Он, канеш, упомянут в документации, но как-то без упора на необходимость аккуратного обхода: если вы кладёте данные по каждому прерыванию TXIS, то по получению состояния STOP регистр передатчика будет хранить предыдущий байт до посинения. И следующая транзакция начнётся с его передачи, а запись в TXDR будет проигнорирована (состояние overrun). Чтобы этого не случалось, при обработке STOPF нужно опорожнять буфер передатчика, выставляя бит I2C_ISR_TXE (и никак иначе).
Наконец-то, передача последовательности байт заработала как надо.