SPI в STM32F0 как не сесть в лужу или правильная инициализация и отправка данных. Часть 2
Продолжая тему работы с интерфейсом SPI в микроконтроллерах STM32F0, хотелось бы затронуть не только инициализацию, но и отправку данных. Решение для одной неочевидной проблемы невозможно найти на просторах интернета. Сложно даже сформулировать запрос в гугле на эту тему. Поэтому можно засесть и ломать голову пару дней, а то и неделю.
Как я уже и говорил, все это не относится к разработчикам, использующим SPL или HAL.
Предположим, что Вы затактировали, настроили интерфейс SPI и хотите проверить его работу. И вроде бы все очевидно - необходимо просто записать один байт данных в регистр DR, а интерфейс SPI все сделает за Вас. Напишем следующую строку:
SPI1->DR = 0x5A;
На логическом анализаторы мы можем наблюдать следующую картину:
Первая мысль - что-то не так с настройками интерфейса, или просто глюк. Перепроверяем все настройки, но там все очевидно. Сложно запутаться в двух регистрах (уточню, мы используем режим с данными длиной 8 бит).
Кажется, будто, используется режим данных 16 бит, и это в принципе, можно проверить. Сконфигурируем CS интерфейса SPI так, чтобы он управлялся железом и генерировал строб между каждой посылкой (читайте в первой части). И снова повторим отправку данных командой, приведенной выше.
Выглядит очень странно. Посылка разбивается на 2 байта - сначала отправляется наши данные, а затем загадочный 0x00. Давайте кое что проверим. Отправим в регистр DR слово из 16 бит.
SPI1->DR = 0x115A;
Посмотрим что нам покажет анализатор:
Теперь совсем все плохо. Интерфейс SPI будто не понимает, что мы используем режим 8 бит и пытается отправить все содержимое регистра DR (регистр имеет длину 16 бит).
Давайте разберемся почему так происходит. Для этого нам понадобится дизассемблер. Посмотрим как выглядит наша строка записи данных в регистр DR:
Я привел пример для обоих вариантов записи данных в регистр DR (8 и 16 бит) при настройка интерфейса в режиме 8 бит. Обратите внимание на команду STRH - она записывает в регистр периферии (т.е. в нашем случае DR) слово (16 бит) в обоих случаях. Даже если Вы напишите вот так:
SPI1->DR = (uint8_t)0x5A;
то ничего не изменится. Проблема стала немного яснее. Нужно попробовать записать только один байт в регистр DR. Сделаем это следующим образом:
*(uint8_t *)&SPI1->DR = 0x5A;
Теперь посмотрим в дизассемблер:
Видим, что команда STRH заменена на STRB, а это значит, что в регистр DR мы записываем ровно один байт данных, который необходимо отправить. Проверим что получилось.
Все работает так, как должно работать.
Такой неоднозначный функционал интерфейса SPI в режиме 8 бит данных может приводить к ошибкам, которые очень тяжело отыскать. Но с другой стороны, может быть это не баг, а фича? Ведь одной командой мы можем записать сразу два байта на передачу по SPI. Об этом стоит подумать.
Комментарии
Отправить комментарий