По многочисленным просьбам мы на данном уроке попытаемся соединить между собой два контроллера 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.
Предыдущий урок Программирование МК STM32 Следующая часть
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Индикатор светодиодный семиразрядный с драйвером MAX7219
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добрый день. Почему в ваших примерах работы с SPI вы не используете Hardware NSS Sygnal вместо «ногодрыга» вывода CS?
Спасибо.
Здравствуйте!
Даже производитель данных контроллеров так советует, когда работаем с ведущим устройством. С ведомым используем.
Добрый день.
Вы можете в конце каждого урока выкладывать законченный проект со всеми файлами?
В частности я устал искать по ссылкам вот эти файлы «max7219.c и max7219.h», которые надо взять из проекта урока 115. Заходишь в 115 урок, а там снова ссылка и т.д.
Можете дать ссылку, где можно скачать эти файлы?
Здравствуйте! Посмотрите во второй части этого урока (153 часть 2) в конце страницы. Полный исходный код (проект) всегда есть в конце каждого урока.