В прошлой статье я предложил достаточно дешевое решения для обмена данными по радиоканалу.
Простой модем для любых целей.+58
19 января 2016 года в 15:07 | Андрей Миловидов Тверь
Статья http://www.parkflyer.ru/blogs/view_entry/13168/

Судя по коментам, народ немного заинтересовался. Попробую разогреть этот интерес. Разберем создание применика из пары "макетка STM32 - nrf24l01".
Для этого понядобятся:
- Arduino Uno (для передатчика, оставим пока ее, т.к. есть более-менее рабочий скетч)
- 2 штуки nrf24l01+ (у меня они с бустером и внешней антенной)
- макетка с stm32f103c8t6 (как на фотке ниже, к примеру)
- программатор ST-LINK/V2 (фирменный, или китайскую копию)
- среда разработки, на выбор. Я буду использовать привычный IAR.


Первым делом качаем IAR, ставим, указываем желаемый режим бесплатного использования. Нам хватит ограничения 32кБайт на код.
Теперь, казалось бы, нужно запускать IAR, создавать проект, настраивать чип и т.д. Но ST решила нам помочь и разработала программу для генерации проектов - STM32Cube. Идем сюда, качем для нашего чипа, ставим. Для установки нужна Java.
Открываем Куб. Создаем новый проект.

Выбираем семейство и модель чипа.

Жмем "ОK", создастся шаблон проекта. Настраиваем выводы - нам нужно подключить шину SPI (для nrf), внешний кварц (есть на плате), 4 выхода для стробов каналов. Для генерации стробов нужен таймер. Их у чипа 4 штуки, можно взять любой. Мне понравился первый. Таймер имеет 4 канала для разных целей, нам нужна генерация ШИМ. SPI шин у чипа две, мне понравилась тоже первая. Настраиваем все так:

UPD. "Забытый" блок.
Теперь надо добавить управление SPI и подключить вывод с прерыванием от модуля. Мне понравились выводы PB0-PB2.
Щелкаем на ногу на картинке, выбираем в выпадашке для PB0 GPIO_EXTI0. Для PB1-PB2 - GPIO_Output. Эти две ноги нужны для сигналов CE и CSN.

Теперь добавляем работу отладочного разъема (SWD). Открываем узел SYS, ставим Serial wire, иначе после старта проца отладка будет отключена аппаратно, и подключиться можно будет только в режиме "Connection under reset".

Выводы настроены, потом можно, при желании, еще добавить что угодно. Идем во вторую вкладку, там настраиваем тайминги.

Обращаем внимание, что получившаяся частота для таймеров - 8 МГц. Это нам пригодится потом, при настройке таймера.
Идем в следующую вкладку, там настройка периферии.

Настраиваем таймер. Нам нужно подавать строб 1..2 мс каждые 20..50 мс. Поэтому сначала разделим входящую частоту на 8 (вбиваем 7, так надо по формуле, которая указана в даташите к чипу), а затем на 50000 (если не нравится, можно любой период выставить). Каждый канал ставим величину строба в 1000 (на картинке видно только 2 канала, нужно выставить у всех).

Настраиваем SPI. Тут все оставляем по-умолчанию, выставляем только делитель по-больше.

Вычисленная частота работы шины отображается строчкой ниже. Удобно, че.
Теперь надо настроить GPIO выводы. Жмем соответствующую кнопку, открывается диаложка, для PB0 выбираем то, на картинке ниже.

Все очень просто - радиомодуль при наступлении события ненадолго сбрасывает линию IRQ в ноль. Остальные 2 ноги работают в режиме push-pull (2х каскадный выход).
Теперь настраиваем прерывание.Жмем кнопку NVIC, открывается диаложка. Там ставим галочку на пункте EXTI line0.

Прерывания у stm группируются не по портам, а по каналам портов, первые 5 ног имеют "именные" прерывания, остальные группируются (6-9, 10-15). Это надо учитывать при проектировании.
Все настроено, сохраняем (до генерации!!), нажимаем кнопку генерации (с шестеренкой), вбиваем название пректа и путь к нему, жмем "Ок".

Готово. Создан проект со всеми нашими настройками, библиотекой периферии и прочими ништяками, вроде dsp функций.
Открываем проект в IAR. Можно сразу скопилять, убедиться, что все работает.

Открываем папку с проектом в проводнике (или где удобно) и добавляем файлы из архива (mirf.rar) в папки Inc и Src файлы mirf .h + nRF24L01.h и mirf.c соответственно. После этого идем в проект. Открываем main.h, подчищаем те коменты, которые не нужны, убираем все, что касается SPI - она будет проинициализирована в mirf.c.
Ну что, поехали кодить...
Сначала подключаем mirf.h в заголовке:
#include "mirf.h" .
Затем добавляем переменную-буфер для хранения данных
uint8_t data[PAYLOAD];
, затем в главном цикле после всех инициализаций, но до основного цикла пишем:
mirfInit();
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
Таким образом, мы запускаем модуль и стробы от таймера. Добавляем полезную функцию и ее объявление:
static void correct1000(uint16_t * val);
...
void correct1000(uint16_t * val)
{
*val += 1000;
if (*val > 2000)
*val = 2000;
if (*val < 1000)
*val = 1000;
}
Она будет корректировать строб, если он после обработки пакета вдруг вылезет за допустимые пределы.
Ну и дописываем основной цикл общения с модемом.
uint16_t temp = 0;
while (1){
mirfCheckStatus(); //проверка на таймаут, здесь же можно воткнуть реализацию failsafe
if (mirfHasIRQ()) //что-то есть, можно пнуть модем
{
mirfResetIRQ(); //снимаем флаг. ВНАЧАЛЕ! Иначе есть вероятность во время обработки проворонить еще пакет. Всякое бывает...
if (mirfDataReady()) //есть входящий пакет
{mirfGetData(data);//вычитываем его
mirfFlushRx(); //зачищаем приемный FIFO
switch (data[0]) //первый байт - тип пакета. здесь он практически не имеет смысла, конструкция представлена для наглядности
{//case 2:
case 1://данные с пульта пришли
data[0] = 101;
temp = (data[1] << 8) + data[2];
correct1000(&temp);
TIM1->CCR1 = temp;
temp = (data[3] << 8) + data[4];
correct1000(&temp);
TIM1->CCR2 = temp;
temp = (data[5] << 8) + data[6];
correct1000(&temp);
TIM1->CCR3 = temp;
temp = (data[7] << 8) + data[8];
correct1000(&temp);
TIM1->CCR4 = temp;
mirfSend(data);
break;
}}else{
mirfFlushRx();//если данных нет. это ушел исходящий пакет, зачищаем буферы
mirfPowerUpRx();//и восстанавливаем режим приема.
}}}
Форматирование редактор угробил, но весь проект можно посмотреть тут: (receiver.rar).
Файл куба: (receiver.ioc).

Как видно, сделать несложный пульт достаточно просто. Таким же способом можно расширить количество пропорциональных каналов до 16 (на чипе 4 таймера), использовтать GPIO, добавить failsafe и т.д.

Если тема интересная, напишите в коментах, будем пилить передатчик, тоже на таком же оборудовании.

Да, есть один маленький нюанс - код успел отлежаться с лета, и вполне мог "протухнуть" (такое бывает, могли попасть правки уже после основных работ). В ближайшее время, как восстановлю стенд, проверю работоспособность.

UPD. Косяки нашел, исправил, дополнил.