На данном уроке мы объединим наши знания по шине SPI и периферии DMA в контроллере STM32 и попробуем применить технологию DMA при передаче данных по интерфейсу SPI. Использовать при этом мы будем библиотеку HAL, которая позволит нам с меньшим трудом организовать этот обмен.
С DMA мы уже знакомы неплохо, причём мы работали с ней не только в режиме передачи из памяти в память, но и в режиме передачи из периферии в память и обратно, но только прерывания мы на ней обрабатывали только в режиме MEM2MEM (из памяти в память). Теперь попрактикуемся с прерываниями и в другом режиме.
Как именно устроен обмен между памятью и периферией в DMA контроллера STM32 аппаратно, мы в данном уроке рассматривать не будем, оставим это для урока по LL, поэтому сразу же попробуем перейти к аппаратной части.
Схема наша со времён прошлого урока не изменилась
Начнём по традиции с ведущего устройства.
Проект для него был сделан из проекта урока 157 с именем SPI_MASTER_INT и новое имя ему было присвоено SPI_MASTER_DMA.
Откроем наш проект в Cube MX и отключим сначала прерывания в SPI1
Включим DMA на приём, настройки канала оставляем по умолчанию, так как последние версии Cube MX автоматически настраивают размерность в соответствии с настройками на шине
Аналогично добавим канал для передачи
Для приёма у нас включился канал 2 и установился режим передачи данных из периферии в память с инкрементированием адреса последней, а для приёма — канал 3 с режимом передачи из памяти в периферию также с инкрементированием адреса памяти.
Сгенерируем проект, откроем его в Keil, настроим программатор на автоперезагрузку, уберём оптимизацию.
Добавим в дерево проекта файл max7219.c, Откроем файл main.c и перейдём к функции-обработчику HAL_SPI_TxRxCpltCallback , в которую мы теперь будем попадать при обработке прерывания от DMA по событиям окончания приёма и передачи полностью всего пакета данных. Вот такая вот универсальная функция.
Причём у нас и код-то в теле данной функции особо не изменится, единственное то, что мы теперь не будем следить за тем, когда у нас передастся последняя посылка, DMA этим займётся самостоятельно. Поэтому мы удалим лишь это условие
if(hspi1.TxXferCount==0)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
fl=1;
}
Перейдём теперь в бесконечный цикл функции main() и изменим там имя вызываемой функции приёма-передачи данных по шине SPI
HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t*) (src_buf+i), (uint8_t *) (dst_buf+i), 128);
Вот, в принципе, и все изменения кода, чтобы перейти от обработки обычных прерываний к прерываниям от DMA. Не правда ли, просто? Вот такой вот HAL!
Соберём наш код, прошьём контроллер ведущего устройства.
Пока не будем спешить отключать MASTER и переходить к ведомому устройству, раз у нас всё так быстро получилось. Сегодня мы попытаемся устранить долго мучающую нас проблему со свечением всех восьмёрок на индикаторе во время подачи питания или после перезагрузки контроллера.
Все восьмёрки и точки на индикаторе — это зажигание всех сегментов
Данная функция заложена в микросхеме MAX7219 для того, чтобы мы могли проверить, что у нас работают все сегменты. По идее, режим тестирования должен отключаться автоматически, но не всегда так происходит. И мы теперь его отключим принудительно. Для этого зайдём в файл max7219.c и в функции инициализации драйвера Init_7219 самым первым делом и отключим тестирование
1 2 3 |
void Init_7219 (void) { Send_7219(0x0F,0x00);//отключим режим тестирования |
Попытаемся теперь собрать код и прошить контроллер
Восьмёрки отключились. Такого ещё никогда не было. Если они включались, то отключить их удавалось только после полного отключения питания, и то не всякий раз. Надеюсь, что больше они нас докучать не будут.
Отключим от компьютера ведущее устройство, подключим ведомое.
Проект для него был также сделан из проекта урока 157 с именем SPI_SLAVE_INT и новое имя ему было присвоено SPI_SLAVE_DMA.
Откроем проект в Cube MX и аналогично ведущему устройству отключим прерывания в SPI1 и включим каналы DMA на приём и передачу
Сгенерируем проект, откроем его в Keil, настроим программатор на автоперезагрузку, уберём оптимизацию.
Добавим в дерево проекта файл max7219.c, Откроем файл main.c и в теле функции-обработчика HAL_SPI_TxRxCpltCallback мы также удалим условие, которое следило за передачей последнего элемента
if(!hspi1.TxXferCount && !fl)
{
fl=1;
}
В функции main() исправим имя вызываемой функции
HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t*) src_buf, (uint8_t *)dst_buf, 128);
В бесконечном цикле тоже исправим
HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t*) (src_buf+i), (uint8_t *)dst_buf, 128);
И, прежде чем собирать код, перейдём в файл maix7219.c и также отключим режим тестирования драйвера в функции его инициализации
1 2 3 |
void Init_7219 (void) { Send_7219(0x0F,0x00);//отключим режим тестирования |
Соберём код, прошьём контроллер, подключим к независимому источнику питания ведущее устройство и, если всё нормально, то у нас начнётся обмен данными между устройствами
Посмотрим также процесс обмена в программе логического анализа
Здесь также всё правильно. Обмен идёт равномерно, в пакете данные передаются непрерывно.
Таким образом, на данном уроке нам удалось передать данные по шине SPI с использованием технологии DMA в режиме передачи между периферией и памятью. Также, что немаловажно, мы устранили дефект включения тестирования драйвера индикатора MAX7219.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Исходный код для ведущего устройства (MASTER)
Исходный код для ведомого устройства (SLAVE)
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Индикатор светодиодный семиразрядный с драйвером MAX7219
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК (нажмите на картинку)
Здравствуйте!
А вы планируете в своих уроках когда нибудь затронуть тему шины CAN?
Очень было бы интересно попробовать её в работе.
Добрый день!
Спасибо за уроки!
Было бы интересно ознакомиться с RS485. (Возможно и с ModBus)
Присоединюсь к предыдущим комментаторам. Очень хочется CAN и RS485 с объяснением как лучше наладить связь между несколькими контроллерами(чтобы один был «мастер» и опрашивал или чтобы все друг друга слушали)
ЗЫ: еще хотелось бы проектик с солидным экраном,чтобы отображал информацию, которая приходит по CANу с разных контроллеров.
Спасибо за уроки.
Добрый день, спасибо Вам за уроки, смотрю постоянно.
Но в процессе работы возник вопрос связанный с назначением портов, подскажите пожалуйста, чем отличаются выводы альтернативные, от того что первоначально выбирает кубик? у меня возникла ситуация, пока не критично, плата не разведена, но на случай если столкнуться с данной проблемой придется.
Соединил два контроллера F103 посредством SPI1, по схеме и конфигу несколько отличной от Вашей, основное отличие — посадил на альтернативные выводы (А15, В3,4,5), МК м/у собой общаются хорошо, добавляю надстройки дальше включаю на мастере I2C1 на выводы В7,6, и если произвести инициализацию (а точнее тактирование i2c) на выводе TX возникает постоянный высокий уровень. С чем это может быть связано, с кубиком, библиотекой hal, с непосредственно с китайскими камнями (оригинал палить дорого пока беру китай)?
я только начинаю, уровень совсем мал, столкнулся с проблемой, на выводах А7,6,5,4 работает с I2C (его я еще не проверял, проходит инициализацию), но на них сидит энкодер, если его перекинуть на альтернативу не будет ли подобных проблем?