STM Урок 153. HAL. SPI. Соединяем два контроллера. Часть 1
По многочисленным просьбам мы на данном уроке попытаемся соединить между собой два контроллера STM32 между собой.
Просьбы данные, правда, в большинстве случаев звучали несколько по-другому. Посетители ресурса очень хотят увидеть урок по работе шины SPI в контроллере STM32 в режиме SLAVE. Но, думаю, соединение между собой двух контроллеров по шине SPI и позволит нам проследить работу шины в режиме SLAVE. А уж после этого мы сможем соединить наш контроллер и с другими контроллерами — AVR или PIC, по которым подобные уроки были, а также и соединиться с каким-либо устройством, которое требует от нас работы в режиме ведомого устройства.
На данном уроке мы будем использовать возможности по работе с шиной SPI с использованием библиотеки HAL, но те, кто увлёкся функционалом библиотеки LL, а также хочет увидеть подробности аппаратной реализации, могут посмотреть уже недавно вышедший урок по шине SPI с использованием данной библиотеки, а также урок, который обязательно вскоре выйдет и по соединению двух контроллеров по SPI, но уже с использованием библиотеки LL.
Благодаря выходу данных уроков по библиотеке LL, нам не придётся здесь подробно останавливаться на изучении аппаратной реализации шины, назначением битов различных регистров. Поэтому можно сразу же приступать к практической части.
Контроллеры для обоих наших соединяемых узлов будут использованы самые простейшие — STM32F103, расположенные на недорогих отладочных платах, что позволит обойтись минимальными накладными расходами на реализацию нашей идеи.
Мониторить работу узлов мы будем по восьмиразрядным индикаторам, установленными на модулях с использованием драйвера MAX7219, причём также подключенным по шине SPI к нашим контроллерам. То есть у нас будут использованы сразу две шины SPI.
Начнём с ведущего устройства. Сначала подключим к нему индикатор по шине SPI. Будет использован SPI2, ножки, использованные для работы с шиной, мы увидим в Cube MX
Также давайте подключим провода для соединения с другим контроллером по шине SPI, не забываем также об общем проводе. Питание не нужно. Итого будет 5 проводов. 4 для SPI и 1 — общий
Откроем проектогенератор Cube MX и создадим там новый проект, выбрав наш контроллер
RCC настроим на работу с кварцевым резонатором

Настроим на работу с программатором по SWD

В разделе Clock Configuration настроим тактирование
Включим шину SPI2, настроим её на 16-разрядный обмен и также выберем оптимальный делитель

У нас включились следующие ножки

Не хватает только ножки SS. Для неё лучше подойдёт программный способ управления. Поэтому настроим соседнюю ножку PB12 на выход

Добавим ей немного скорости

Включим также SPI1 и настроим его подобным образом

У нас включатся вот эти ножки

Способом, аналогичным шине SPI2, настроим ножку SS


Настроим свойства проекта
Сгенерируем проект, откроем его в Keil, включим автоперезагрузку и уберём оптимизацию.
Файлы для работы с индикатором max7219.c и max7219.h возьмём из проекта урока 115 с именем NRF24_RX_00. Данные файлы мы скопируем в соответствующие им папки нашего нового проекта.
Подключим к дереву проекта файл max7219.c.
Также подключим в файле main.c нашу библиотеку
|
1 2 |
/* USER CODE BEGIN Includes */ #include "max7219.h" |
В функции main() добавим переменную для счёта
|
1 2 |
/* USER CODE BEGIN 1 */ uint16_t i; |
Установим высокий логический уровень на ножках SS обоих шин SPI, подождём немного, вызовем функцию инициализации микросхемы индикатора и попытаемся вывести цифру на его табло
|
1 2 3 4 5 6 7 |
/* USER CODE BEGIN 2 */ i=0; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); HAL_Delay(100); Init_7219(); Number_7219(87654321); |
Если мы сейчас соберём код и попытаемся его прошить в контроллер, то у нас ничего работать не будет. А не будет потому, что мы выставили режим обмена 16-разрядный, а в коде используется отдельная передача адреса и данных регистров микросхемы. Теперь мы всё это будем отправлять сразу. Поэтому идём в файл max7219.c и сначала удалим глобальный массив
uint8_t aTxBuf[1]={0};
А в функции Send_7219 передадим сразу всё.
Для начала мы там положим всё в 16-разрядную целочисленную переменную
|
1 2 3 4 |
void Send_7219 (uint8_t rg, uint8_t dt) { uint16_t dtt = (uint16_t)rg << 8 | dt; cs_set(); |
Вот это
aTxBuf[0]=rg;
и это
aTxBuf[0]=dt;
HAL_SPI_Transmit (&hspi2, (uint8_t*)aTxBuf, 1, 5000);
удалим.
А здесь передадим в функцию адрес переменной со всеми данными
HAL_SPI_Transmit (&hspi2, (uint8_t*) &dtt, 1, 5000);
Вот теперь у нас всё будет работать

Следующая задача — передать что-нибудь ведомого устройству.
Сначала немного подправим библиотеку индикатора.
В функции вывода числа в левую половину индикатора NumberL_7219, чтобы не выводились лидирующие нули при малых значениях
|
1 2 3 4 |
uint8_t i=4; if(n<1000) Send_7219(8,0xF);//символ пустоты if(n<100) Send_7219(7,0xF);//символ пустоты if(n<10) Send_7219(6,0xF);//символ пустоты |
Добавим аналогичную функцию вывода числа в правую половину индикатора
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//------------------------------------------------------- void NumberR_7219 (volatile int n) { uint8_t ng=0;//переменная для минуса if(n<0) { ng=1; n*=-1; } uint8_t i=0; if(n<1000) Send_7219(4,0xF);//символ пустоты if(n<100) Send_7219(3,0xF);//символ пустоты if(n<10) Send_7219(2,0xF);//символ пустоты do { Send_7219(++i,n%10);//символ цифры n/=10; } while(n); if(ng) { Send_7219(i+1,0x0A);//символ - } } //------------------------------------------------------- |
Создадим для данной функции прототип в заголовочном файле, перейдём в файл main.c в функцию main() и после вывода цифры на экран подождём 2 секунды. Дисплей нам очищать не надо, так как мы всё лишнее теперь гасим в самих функциях вывода чисел в тетрады индикатора
|
1 2 |
Number_7219(87654321); HAL_Delay(2000); |
Создадим глобальный буфер для приёма данных от устройства SLAVE
|
1 2 |
/* USER CODE BEGIN PV */ uint8_t RxBuf[2] = {0}; |
В бесконечном цикле функции main() мы будем наращивать наш счётчик до 9999 и затем обнулять. Результат мы передадим ведомому, заодно на ходу принимая у него аналогичное число в массив RxBuf, а затем выведем пока передаваемое число в правой тетраде индикатора, и потом подождём десятую долю секунды
|
1 2 3 4 5 6 7 8 |
/* USER CODE BEGIN 3 */ i++; if(i>9999) i=0; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(&hspi1, (uint8_t*) &i, (uint8_t *)RxBuf, 1, 5000); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4 , GPIO_PIN_SET); NumberR_7219(i); HAL_Delay(100); |
Соберём код, прошьём контроллер. Через 2 секунды правый счётчик начнёт считать. А на левом пока останется левая половина выведенного нами числа

Перейдём к SLAVE.
Аналогичным образом подключим индикатор и посадим нашу плату на маленькую макетку, так как мы ещё будем подключать логический анализатор на шину SPI1
Подключим к нашему ведущему устройству провода от шины SPI ведущего, не забывая об общем проводе. Ножки будут совпадать на MASTER и SLAVE, PA4 подключится к PA4, PA5 — к PA5, PA6 — к PA6, PA7 — к PA7

В следующей части нашего урока мы создадим и настроим проект для устройства SLAVE и на практике проверим работу нашего кода по передаче данных между двумя контроллерами посредством шины SPI.
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Индикатор светодиодный семиразрядный с драйвером MAX7219
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)









Добрый день. Почему в ваших примерах работы с SPI вы не используете Hardware NSS Sygnal вместо «ногодрыга» вывода CS?
Спасибо.
Здравствуйте!
Даже производитель данных контроллеров так советует, когда работаем с ведущим устройством. С ведомым используем.
Добрый день.
Вы можете в конце каждого урока выкладывать законченный проект со всеми файлами?
В частности я устал искать по ссылкам вот эти файлы «max7219.c и max7219.h», которые надо взять из проекта урока 115. Заходишь в 115 урок, а там снова ссылка и т.д.
Можете дать ссылку, где можно скачать эти файлы?
Здравствуйте! Посмотрите во второй части этого урока (153 часть 2) в конце страницы. Полный исходный код (проект) всегда есть в конце каждого урока.