Ви не увійшли.
Сторінки 1
"Зовсім не зрозуміло, як це логіка роботи залежить від виду конструкції розгалуження" - легко:
if (Serv==1) { // Проверка для какого канала управления производится формирование сигнала(канал №1)
if (Shag==1) { // Проверка что нужно сделать, шаг 1 установить высокий уровень и запрограмировать
OCR1A = Serv_1_1; // регистр совпадений для формирования требуемой длины управляющего импулься
digitalWrite(2, HIGH); // Установка на выводе 2 (канал №1) высокого уровня, начало формирования импульса
Shag=2; // Величина Serv_1_1, длина импульса вычисляется в основном цикле
goto Of_canel_1; // Для того чтобы при следующем прерывании перети к шагу 2
}
if (Shag==2) {
OCR1A = Serv_1_2; // Величина Serv_1_2, время до формирования на следуещем канале импульса вычисляется в основном цикле
digitalWrite(2, LOW); // Шаг 2 - устанавливаем низкий уровень на выводе 2(канал 1)
Shag=1; // Для того чтобы правильно сформировать начало импульса на следующем канале
Serv=2; // Для того чтобы перейти к формированию сигнала на выводе 4(канал №2)
}
} // Конец формирования сигнала для канала 1
Of_canel_1:
// Получился косяк который пришлось исправлять через GOTO, пришлось переписывать на Switch - case - break;
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
if (Serv==2) {.........Я просто протупив - if виконуються послідовно, а я у для Serv 2-му циклі робив Serv=2;, після чого шов оператор if (Serv==2) {......... який за задумом мав виконуватися лише при наступному преривані...
Тільки с"огодні дійшло що потрібно було не з першого канала почінати, а з 5...
Виходило що спочатку формуються 5 импульсів, а потім 1 довгий, поки дивився сигнал на одному каналі усе чудово, а потім почав вираховувати затримки для таймерів, а воно не стикується...
Дякую за роз"яснення по роботі коду(я так розумію це асемблер), не факт що використаю у остаточній версії, але 100% причеплю у кінець скетчу на усякий випадок!!!
№70 - Приблизно так я код і переписав.
З прериванями теж зрозуміло - де може вилізти боком теж, я маю надію схитрити з часовими затримками і перейти на byte - Serv_1_1..., поки що для тестування проканало і так.
Доповнення - обробку преривання довелося знов переписати, при тестуваны осцилографом виявилось що наступний імпульс формується відразу після закінчення формування попередн"ого.
Знов довелося перейти на switch case
Хто може ЛЮДСКОЮ мовою пояснити як працює цей код:
static const uint8_t pins[4] = { 2, 4, 7, 8 };
static volatile uint16_t ocr[4][2];
ISR ...
{
uint8_t idx = Serv;
Serv = (idx + 1) % 8;
uint8_t channel = idx / 2;
uint8_t state = idx % 2;
digitalWrite(pins[channel], !state);
OCR1A = ocr[channel][state];
}Бо моїх знань вистачіло щоб зрозуміти масив з номерами виводів, запис 0/1 на відповідний вивід і програмування таймеру...
№47 - може окремий if і виконується швидше(я спочатку їх і використав), але switch case дозволив обійтись без goto...
№49 - "для таких ПрофесіАналів у програмувані як я важливіше розуміти як воно працює"... Ваш шматок коду вигляда красиво, але якщо я ще приблизно розумію як він працює, то самостійно змінити за потреби 100% не зможу!!!
"потрібно оновлювати з вимкеними перериваннями" - детальніше можна, у поточній редакції word Serv_1_1
Налаштуваня таймера дані виключно для розуміня чого я там накарябав, налаштування виконується ОДИН РАЗ ще до початку основного цикла і НІЯК на швидкість виконаня не вплиива...
Пращює і добре - для таких ПрофесіАналів у програмувані як я важливіше розуміти як воно працює, а не щоб було красиво.
Ось цей шматок таким залишив навмистно:
OCR1A = 40; // установка регистра совпадения
TCCR1B |= (1 << WGM12); // включить CTC режим
TCCR1B |= (0 << CS10); // Установить биты на коэффициент деления
TCCR1B |= (1 << CS11);
TCCR1B |= (0 << CS12);Це с"огодні в мене такі налаштування таймеру такі, а якщо завтра захочу встановити інші?
Так усе у одному місці і з пам"яткою "що за що відповіда"....
Мене більше турбує обробка преривання, до ц"ого часу я обходився оператором if, і на комбінацію switch case прейшов щоб не пхати у код goto .
Винахід велосипеда - формування сигналу у прериванях методом зміни регістру співпадіня.
Налаштування таймера 1:
cli(); // отключить глобальные прерывания
TCCR1A = 0; // установить регистры в 0
TCCR1B = 0;
// Задание частоты прерываний выполняется програмированием регистров OCR1A, TCCR1B CS10, TCCR1B CS11,TCCR1B CS12
// CS12=0, CS11=0, CS10=1 делитель 0
// CS12=0, CS11=1, CS10=0 делитель 8
// CS12=0, CS11=1, CS10=1 делитель 64
// CS12=1, CS11=0, CS10=0 делитель 256
// CS12=1, CS11=0, CS10=1 делитель 1024
// Для 1000 прерываний в секунду CS12=0, CS11=0, CS10=1, OCR1A = 319, на выходе импульсы с частотой почти 50 Гц
// Неприятный сюрприз - 8 МГц ардуинка 1000 прерываний не тянет :(
// Пришлось уменьшить число временых интервалов с 1000 до 500, а число "уровней" PPM сигнала до 32 :(
// От отправки даных в СОМ порт вообще пришлось отказатся :(
OCR1A = 40; // установка регистра совпадения
TCCR1B |= (1 << WGM12); // включить CTC режим
TCCR1B |= (0 << CS10); // Установить биты на коэффициент деления
TCCR1B |= (1 << CS11);
TCCR1B |= (0 << CS12);
TIMSK1 |= (1 << OCIE1A); // включить прерывание по совпадению таймера
sei(); // включить глобальные прерыванияПідпрограмма:
{
// Принцип формирования импульсов управления - импульсы формируются ПООЧЕРЕДНО 50 раз в секунду, сначала устанавливается высокий уровень на ножке управления
// сервопривода 1, выдерживается требуемый интервал времени, устанавливается низкий уровень.
if (Serv==1) { // Проверка для какого канала управления производится формирование сигнала(канал №1)
switch (Shag) {
case 1:
digitalWrite(2, HIGH); // Установка высокого уровня на выводе 2
OCR1A = Serv_1_1; // Установка значения регистра совпадения - формирование длительности сигнала,
// значение вычисляется в основном цикле
Shag=2; // Присвоение переменно Shag значения 2 - чтобы при следующем прерывании сработал 2-й блок
break; // Выход из оператора case без выполнения 2-го блока
case 2:
digitalWrite(2, LOW); // Установка низкого уровня на выводе 2
OCR1A = Serv_1_2; // Формирование паузы перед началом формирования импулься на выводе 4
// значение вычисляется в основном цикле
Shag=1; // Присвоение переменно Shag значения 1 - чтобы при следующем прерывании сработал 1-й блок
Serv=2; // Присвоение переменно Serv значения 2 - чтобы при следующем прерывании перейти к формированию
// импулься для второго сервопривода
break;
}
} // Конец формирования сигнала для канала 1
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
if (Serv==2) { // Все тоже самое для канала 2, меняется только номер вывода на котором формируется сигнал
switch (Shag) {
case 1:
digitalWrite(4, HIGH);
OCR1A = Serv_2_1;
Shag=2;
break;
case 2:
digitalWrite(4, LOW);
OCR1A = Serv_2_2;
Shag=1;
Serv=3;
break;
}
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
if (Serv==3) { // Все тоже самое для канала 3, меняется только номер вывода на котором формируется сигнал
switch (Shag) {
case 1:
digitalWrite(7, HIGH);
OCR1A = Serv_3_1;
Shag=2;
break;
case 2:
digitalWrite(7, LOW);
OCR1A = Serv_3_2;
Shag=1;
Serv=4;
break;
}
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
if (Serv==4) { // Все тоже самое для канала 4, меняется только номер вывода на котором формируется сигнал
switch (Shag) {
case 1:
digitalWrite(8, HIGH);
OCR1A = Serv_4_1;
Shag=2;
break;
case 2:
digitalWrite(8, LOW);
OCR1A = Serv_4_2;
Shag=1;
Serv=5;
break;
}
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Для пробы используется только 4-ре канала, 5-й добавлен чтобы потом не мучится с вычислением задержек
if (Serv==5) { // Все тоже самое для канала 5, меняется только номер вывода на котором формируется сигнал
switch (Shag) {
case 1:
digitalWrite(8, LOW);
//digitalWrite(8, HIGH); // К этому времени на канале 4 (вывод)уже установлен низкий уровень, чтобы не возица потом
OCR1A = Serv_5_1; // вставлена пустышка - потом одну строку закоментировать, другую разкоментировать
Shag=2; // и выбрать на каком пине будет генерация сигнала
break;
case 2:
digitalWrite(8, LOW);
OCR1A = Serv_5_2;
Shag=1;
Serv=1; // Сигнал сформирован на вссех 5 каналах - возвращаемся к каналу 1
break; // частота сигнала определяется сумой задержек сформированим програмирование регистра совпадений
} // таймера 1 и делителем частоты таймера 1
}
}Осцилограф підтвердив працездатність, прошу покритикувати, особливо в частині швидкості виконаня.
№18 По кварц не скажу, занадто дрібний, але при переносі коду на плату 16 МГц частота подвоюється, відповідно встановлено 8 МГц...
Так що з вашим кодом, що з стандартною біліотекою Servo є проблеми з синхронізацією, якщо осцилограф поганий то проблеми з синхронізацією мали бути і при виконані мого коду...
"56 кроків", 40% і т.д. влаштовує не дуже, основний цикл виконується лише з частотою 30 Гц, занадто повільно, а ще 100% будуть доповнення...
№20 digitalwrite виконуються 8 раз за цикл, а 6 операторів "if" - 500 раз, багато виграю часу?
Сенс простий - напруга живлення 3,3 вольта, один стабілізатор і для Ардуіни, і для nRF24L01...
Можна, тільки я не програміст, а Ардуіну більшь-меньшь нормально знаю...
№21 Щось подібне вже почав писати, але прикладів з поясненнями нема, так що "Винайдення велосипеда"...
Прожерливий код який формує суму стабільну осцилограму - частота виклику 25 кГц...
ISR(TIMER1_COMPA_vect)
{
Time = Time+1;
if (Time==1) {
// Высокий уровень выставляется на всех выходах одновременно
digitalWrite(2, HIGH); // Установка на выводе 2 высокого уровня, начало формирования импульса
digitalWrite(4, HIGH); // Установка на выводе 4 высокого уровня, начало формирования импульса
digitalWrite(7, HIGH); // Установка на выводе 7 высокого уровня, начало формирования импульса
digitalWrite(8, HIGH); // Установка на выводе 8 высокого уровня, начало формирования импульса
}
// Низкий уровень выставляется для каждого вывода индивидуально в зависимости от величины управляющего сигнала
if (Time==PD_2) {digitalWrite(2, LOW);} // Установка на выводе 2 низкого уровня, конец формирования импульса
if (Time==PD_4) {digitalWrite(4, LOW);} // Установка на выводе 4 низкого уровня, конец формирования импульса
if (Time==PD_7) {digitalWrite(7, LOW);} // Установка на выводе 7 низкого уровня, конец формирования импульса
if (Time==PB_0) {digitalWrite(8, LOW);} // Установка на выводе 8 низкого уровня, конец формирования импульса
if (Time==500) {
Time = 0; // Формирование паузы и обнуление переменой Time
}
}Те що генерує ваша:
Далі відео:
Налаштування осцилографа не змінювалися, заливав прошивку, переставляв щуп.
Синій сигнал - китайский сервотестер
Ваш код
https://www.youtube.com/watch?v=z_B7b96iI2c
Стандартна Servo
https://youtu.be/S91GYQ1sLrc
Моя, сжера 40% ресурсу
https://youtu.be/Sgz3m2NhGO0
Завтра можу дістати екшен камеру і зняти усе одним відео
При спробі завантажити код виходить якась ху.....
Код у попередн"ому перегляді відображається нормально, дата остан"ого повідомлення на форумі змінюється, але саме повідомлення не відображається
/*
Пример скетча "управление с телеметрией", то есть модуль ПЕРЕДАТЧИК
шлёт на ПРИЁМНИК команды управления, ПРИЁМНИК при получении пакета данных
отправляет ПЕРЕДАТЧИКУ пакет телеметрии (какие-то свои данные). ПЕРЕДАТЧИК
эти данные принимает. Вот такие пироги. Также в этом примере реализован
расчёт RSSI (процент ошибок связи), на основании которого можно судить о
качестве связи между модулями.
*/
// https://www.youtube.com/watch?v=ACVtKDJVXS4 https://www.youtube.com/watch?v=p0v2vSmajx8&t=1s https://www.youtube.com/watch?v=8xGrPpXyc5w
// ЭТО СКЕТЧ ПРИЁМНИКА!!!
// ОБНАРУЖЕНА ПОДЛЯНКА - видимо из зм конфликта прерываний с nRF24L01 библиотека Servo генерирует зашумленный сигнал, сервомашинки
// "проглатывают", а вот драйвер BLDC работать отказывается.
// Тестовая программа сервотестера/ШИМ сигнел генерирует чистый - драйвер BLDC работает без проблем ![]()
//--------------------- НАСТРОЙКИ ------------------------------
#define CH_NUM 0x60 // номер канала (должен совпадать с передатчиком)
#define SIG_POWER RF24_PA_MIN // УРОВЕНЬ МОЩНОСТИ ПЕРЕДАТЧИКА
// На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
#define SIG_SPEED RF24_1MBPS // ---------------------СКОРОСТЬ ОБМЕНА--------------------------
// На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
// должна быть одинакова на приёмнике и передатчике!
// при самой низкой скорости имеем самую высокую чувствительность и дальность!!
// ВНИМАНИЕ!!! enableAckPayload НЕ РАБОТАЕТ НА СКОРОСТИ 250 kbps!
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include <Servo.h>
RF24 radio(9, 10); // Задание выводов к которым подключен модуль Wi-Fi + 11, 12, 13(аппаратный интерфейс)
//--------------------- ПЕРЕМЕННЫЕ для работы с nRF24L01----------------------
byte pipeNo;
byte address[][6] = {"1Node", "2Node", "3Node", "4Node", "5Node", "6Node"}; // возможные номера труб
int recieved_data[16]; // массив принятых данных
int telemetry[2]; // массив данных телеметрии (то что шлём на передатчик)
// !!! Больше 2 ячеек масив делать нельзя - перестает работать!!!
// Предположительно ограничение на длину "обратной посылки"
//----------------Для примера будеть только 4 пина, цифровой(вкл/выкл), аналоговый - ШИМ, анологовый сервопривод, аналоговый - BLDC для безколекторного двигателя
// Тип переменой int используется чтобы не изгалять с переводом в byte, для теста сойдет
int Relay = 2; // Релейный выход
byte Shim = 3; // Проба ШИМ, работает только на выводах 3, 5, 6, 9, 10, 11, но с выводом на ноги 5 и 6
// могут быть конфликты с внутреними таймерами, а пин 11 использует nRF24L01 !!!
//---------------При использовании библиотети Servo работа ШИМ на выводах 9, 10 невозможна!!!!!!!!!!
Servo SerV; // Тест сервопривода
Servo BldS; // Тест работы драйвера безколекторного двигателя
byte A_0; // Переменные для промежуточных результатов, чтобы не менять масив с приёмника
byte A_1;
byte A_2;
byte A_3;
byte A_4;
byte A_5;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
void setup() {
Serial.begin(9600); // Настройка СОМ порта
//radioSetup(); //Ссылка на подпрограмму настройки радиомодуля, закоментированно.
//---------------------------------------------Настройка радиомодуля, в оригинале какого то х... были выполнены в виде подпрограммы----------------------------
radio.begin(); // активировать модуль
radio.setAutoAck(1); // режим подтверждения приёма, 1 вкл 0 выкл
radio.setRetries(0, 15); // (время между попыткой достучаться, число попыток)
radio.enableAckPayload(); // разрешить отсылку данных в ответ на входящий сигнал
radio.setPayloadSize(32); // размер пакета, байт
radio.openReadingPipe(1, address[0]); // хотим слушать трубу 0
radio.setChannel(CH_NUM); // выбираем канал (в котором нет шумов!)
radio.setPALevel(SIG_POWER); // уровень мощности передатчика
radio.setDataRate(SIG_SPEED); // скорость обмена
// должна быть одинакова на приёмнике и передатчике!
// при самой низкой скорости имеем самую высокую чувствительность и дальность!!
radio.powerUp(); // начать работу
radio.startListening(); // начинаем слушать эфир, мы приёмный модуль
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
pinMode (Relay, OUTPUT); // Релейный выход - любой свободный пин
pinMode (Shim, OUTPUT); // Выход ШИМ, по факту в приёмнике доступны только 3, 5, 6
//-------------------------Настройка выходов управление сервоприводов/драйверов BLDC----------------------------------------------------------------------------
SerV.attach(6,150,2400); // Используется пин 6 // Насттройки длительности импульса НЕСТАНДАРТНЫЕ - последствия использования
BldS.attach(7,150,2400); // Используется пин 7 // Ардуино Про 8 Мгц, 3,3 вольт...
// Кроме того китайцы сервопривода 180 градусные в 90 градусные превратили програмно!!!
SerV.write(0); // Даннае строки для калибровки драйвера BLDC, цифры нужно уточнять эксперементально!!!
BldS.write(0);
delay (2000);
}
void loop() {
// Приём пакета с пульта управления и отправка телеметрии
//while (radio.available(&pipeNo)) { // слушаем эфир
// radio.read(&recieved_data, sizeof(recieved_data)); // чиатем входящий сигнал
// telemetry[0] = analogRead(A5); // формируем пакет данных телеметрии (напряжение АКБ, скорость, температура...)
// telemetry[1] = 20; // для пробы меряем собственное питание
// radio.writeAckPayload(pipeNo, &telemetry, sizeof(telemetry)); // отправляем пакет телеметрии
//}
//Serial.print(" А0: "); Serial.print(recieved_data[0]); // Отправка масива с АЦП в СОМ порт, для отладки
//Serial.print(" А1: "); Serial.print(recieved_data[1]);
//Serial.print(" А2: "); Serial.print(recieved_data[2]);
//Serial.print(" А3: "); Serial.print(recieved_data[3]);
//Serial.print(" А4: "); Serial.print(recieved_data[4]);
//Serial.print(" А5: "); Serial.print(recieved_data[5]);
//Serial.print(" Pin 7: "); Serial.println(recieved_data[6]); // Перенос строки
//----------------------Проба вывода управляющих сигналов------------------------------------------------------------------------------------------------------
//digitalWrite(Relay,recieved_data[6]); // Управление цифровым пином, для теста он в 6 ячейке масива
//A_0 = map(recieved_data[0],0,1023,0,255); // Преообразование из 0...1023 выдаваемого АЦП в 0...255 принимаемого ШИМ
//analogWrite(Shim,A_0); // Запись в регистр отвечающий за ШИМ
//A_1 = map (recieved_data[1],0,1023,0,90); // Преобразование для сервомашинки 0...1023 в 0...90,последствия Ардуино Про 8 Мгц, 3,3 вольт...
//SerV.write(A_1); // Передача библиотеке Servo данных для сервопривода
//A_2 = map (recieved_data[2],0,1023,0,90); // Преобразование для сервомашинки 0...1023 в 0...900,последствия Ардуино Про 8 Мгц, 3,3 вольт...
//BldS.write(A_2); // Передача библиотеке Servo данных для драйвера BLDS
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
}
/*
Пример скетча "управление с телеметрией", то есть модуль ПЕРЕДАТЧИК
шлёт на ПРИЁМНИК команды управления, ПРИЁМНИК при получении пакета данных
отправляет ПЕРЕДАТЧИКУ пакет телеметрии (какие-то свои данные). ПЕРЕДАТЧИК
эти данные принимает. Вот такие пироги. Также в этом примере реализован
расчёт RSSI (процент ошибок связи), на основании которого можно судить о
качестве связи между модулями.
*/
// https://www.youtube.com/watch?v=ACVtKDJVXS4 https://www.youtube.com/watch?v=p0v2vSmajx8&t=1s https://www.youtube.com/watch?v=8xGrPpXyc5w
// ЭТО СКЕТЧ ПРИЁМНИКА!!!
// ОБНАРУЖЕНА ПОДЛЯНКА - видимо из зм конфликта прерываний с nRF24L01 библиотека Servo генерирует зашумленный сигнал, сервомашинки
// "проглатывают", а вот драйвер BLDC работать отказывается.
// Тестовая программа сервотестера/ШИМ сигнел генерирует чистый - драйвер BLDC работает без проблем :(
//--------------------- НАСТРОЙКИ ------------------------------
#define CH_NUM 0x60 // номер канала (должен совпадать с передатчиком)
#define SIG_POWER RF24_PA_MIN // УРОВЕНЬ МОЩНОСТИ ПЕРЕДАТЧИКА
// На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
#define SIG_SPEED RF24_1MBPS // ---------------------СКОРОСТЬ ОБМЕНА--------------------------
// На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
// должна быть одинакова на приёмнике и передатчике!
// при самой низкой скорости имеем самую высокую чувствительность и дальность!!
// ВНИМАНИЕ!!! enableAckPayload НЕ РАБОТАЕТ НА СКОРОСТИ 250 kbps!
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include <Servo.h>
RF24 radio(9, 10); // Задание выводов к которым подключен модуль Wi-Fi + 11, 12, 13(аппаратный интерфейс)
//--------------------- ПЕРЕМЕННЫЕ для работы с nRF24L01----------------------
byte pipeNo;
byte address[][6] = {"1Node", "2Node", "3Node", "4Node", "5Node", "6Node"}; // возможные номера труб
int recieved_data[16]; // массив принятых данных
int telemetry[2]; // массив данных телеметрии (то что шлём на передатчик)
// !!! Больше 2 ячеек масив делать нельзя - перестает работать!!!
// Предположительно ограничение на длину "обратной посылки"
//----------------Для примера будеть только 4 пина, цифровой(вкл/выкл), аналоговый - ШИМ, анологовый сервопривод, аналоговый - BLDC для безколекторного двигателя
// Тип переменой int используется чтобы не изгалять с переводом в byte, для теста сойдет
int Relay = 2; // Релейный выход
byte Shim = 3; // Проба ШИМ, работает только на выводах 3, 5, 6, 9, 10, 11, но с выводом на ноги 5 и 6
// могут быть конфликты с внутреними таймерами, а пин 11 использует nRF24L01 !!!
//---------------При использовании библиотети Servo работа ШИМ на выводах 9, 10 невозможна!!!!!!!!!!
Servo SerV; // Тест сервопривода
Servo BldS; // Тест работы драйвера безколекторного двигателя
byte A_0; // Переменные для промежуточных результатов, чтобы не менять масив с приёмника
byte A_1;
byte A_2;
byte A_3;
byte A_4;
byte A_5;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
void setup() {
Serial.begin(9600); // Настройка СОМ порта
//radioSetup(); //Ссылка на подпрограмму настройки радиомодуля, закоментированно.
//---------------------------------------------Настройка радиомодуля, в оригинале какого то х... были выполнены в виде подпрограммы----------------------------
radio.begin(); // активировать модуль
radio.setAutoAck(1); // режим подтверждения приёма, 1 вкл 0 выкл
radio.setRetries(0, 15); // (время между попыткой достучаться, число попыток)
radio.enableAckPayload(); // разрешить отсылку данных в ответ на входящий сигнал
radio.setPayloadSize(32); // размер пакета, байт
radio.openReadingPipe(1, address[0]); // хотим слушать трубу 0
radio.setChannel(CH_NUM); // выбираем канал (в котором нет шумов!)
radio.setPALevel(SIG_POWER); // уровень мощности передатчика
radio.setDataRate(SIG_SPEED); // скорость обмена
// должна быть одинакова на приёмнике и передатчике!
// при самой низкой скорости имеем самую высокую чувствительность и дальность!!
radio.powerUp(); // начать работу
radio.startListening(); // начинаем слушать эфир, мы приёмный модуль
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
pinMode (Relay, OUTPUT); // Релейный выход - любой свободный пин
pinMode (Shim, OUTPUT); // Выход ШИМ, по факту в приёмнике доступны только 3, 5, 6
//-------------------------Настройка выходов управление сервоприводов/драйверов BLDC----------------------------------------------------------------------------
SerV.attach(6,150,2400); // Используется пин 6 // Насттройки длительности импульса НЕСТАНДАРТНЫЕ - последствия использования
BldS.attach(7,150,2400); // Используется пин 7 // Ардуино Про 8 Мгц, 3,3 вольт...
// Кроме того китайцы сервопривода 180 градусные в 90 градусные превратили програмно!!!
SerV.write(0); // Даннае строки для калибровки драйвера BLDC, цифры нужно уточнять эксперементально!!!
BldS.write(0);
delay (2000);
}
void loop() {
// Приём пакета с пульта управления и отправка телеметрии
//while (radio.available(&pipeNo)) { // слушаем эфир
// radio.read(&recieved_data, sizeof(recieved_data)); // чиатем входящий сигнал
// telemetry[0] = analogRead(A5); // формируем пакет данных телеметрии (напряжение АКБ, скорость, температура...)
// telemetry[1] = 20; // для пробы меряем собственное питание
// radio.writeAckPayload(pipeNo, &telemetry, sizeof(telemetry)); // отправляем пакет телеметрии
//}
//Serial.print(" А0: "); Serial.print(recieved_data[0]); // Отправка масива с АЦП в СОМ порт, для отладки
//Serial.print(" А1: "); Serial.print(recieved_data[1]);
//Serial.print(" А2: "); Serial.print(recieved_data[2]);
//Serial.print(" А3: "); Serial.print(recieved_data[3]);
//Serial.print(" А4: "); Serial.print(recieved_data[4]);
//Serial.print(" А5: "); Serial.print(recieved_data[5]);
//Serial.print(" Pin 7: "); Serial.println(recieved_data[6]); // Перенос строки
//----------------------Проба вывода управляющих сигналов------------------------------------------------------------------------------------------------------
//digitalWrite(Relay,recieved_data[6]); // Управление цифровым пином, для теста он в 6 ячейке масива
//A_0 = map(recieved_data[0],0,1023,0,255); // Преообразование из 0...1023 выдаваемого АЦП в 0...255 принимаемого ШИМ
//analogWrite(Shim,A_0); // Запись в регистр отвечающий за ШИМ
//A_1 = map (recieved_data[1],0,1023,0,90); // Преобразование для сервомашинки 0...1023 в 0...90,последствия Ардуино Про 8 Мгц, 3,3 вольт...
//SerV.write(A_1); // Передача библиотеке Servo данных для сервопривода
//A_2 = map (recieved_data[2],0,1023,0,90); // Преобразование для сервомашинки 0...1023 в 0...900,последствия Ардуино Про 8 Мгц, 3,3 вольт...
//BldS.write(A_2); // Передача библиотеке Servo данных для драйвера BLDS
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
}Скетч повний, пробував закоментовувати "зайве" - не допомага...
№9 - бібліотека Servo.h працює по таймеру, тому що там у основному циклі неважливо, просто буде виконуватися повільніше.
BLDC - Китайский драйвер для квадрокоптера 1-2S, але драйвер з продвинутих...
Нечіткість сигналу - той імпульс що по центру генерується стабільно, а от той що зліва "мерегтить" і "плава" ліворучь-праворучь..
№10 - Вже й так спробував, але тупонув - на усіх 4-х каналах сигнал синхронізовано, вийшло накладно по ресурсам, с"огодні буду пробувати переписати для послідовного формування імпульсів.
По ресурсам/комунікації нема нічого "важкого" і критичного по часу виконання..
№11 - контроллер з управлінням PWM, але контролер продвинутий - те що зжирають Китайскі серви його не влаштовує...
Як відповідати індивідуально не знаю, тому формат відповіді: № повідомлення - відповідь..
№3 - Atmega328P, скетчу фактично нема - у основному циклі лише SerV.attach(6,150,2400); і SerV.write(0); Взяв іншу плату з кварцем на 16 МГц, залив той самий скетч - на осцилографі чіткий сигнал...
№4 - варіант з таймером якраз колупаю, підозрюю що вийде щось типу "лікування зубів через сраку"... Додаткові модулі не варіант, принципово, тоді вже краще стандартну плату квадрика з Али..
№5 - критерій простий, на платі з кварцем 16 МГц усе працює, на платі де кварц 8 МГц, осцилограмма коса, драйвер двигуна калібраватися і переходити у робочій режим не бажа... Напруги сигналу 100% вистача - перевіренно!!!
№6 - "₴#@но sad" чудово синхронізується сигналом Китайского сервотестера і з платою де кварц 16 МГц...
Плюнувши на стандартну бібліотеку Servo.h зібрав з шматків 100% робочій шматок:
Time = Time+1;
if (Time==1) {
// Высокий уровень выставляется на всех выходах одновременно
digitalWrite(2, HIGH); // Установка на выводе 2 высокого уровня, начало формирования импульса
digitalWrite(4, HIGH); // Установка на выводе 4 высокого уровня, начало формирования импульса
digitalWrite(7, HIGH); // Установка на выводе 7 высокого уровня, начало формирования импульса
digitalWrite(8, HIGH); // Установка на выводе 8 высокого уровня, начало формирования импульса
}
// Низкий уровень выставляется для каждого вывода индивидуально в зависимости от величины управляющего сигнала
if (Time==PD_2) {digitalWrite(2, LOW);} // Установка на выводе 2 низкого уровня, конец формирования импульса
if (Time==PD_4) {digitalWrite(4, LOW);} // Установка на выводе 4 низкого уровня, конец формирования импульса
if (Time==PD_7) {digitalWrite(7, LOW);} // Установка на выводе 7 низкого уровня, конец формирования импульса
if (Time==PB_0) {digitalWrite(8, LOW);} // Установка на выводе 8 низкого уровня, конец формирования импульса
if (Time==500) {
Time = 0; // Формирование паузы и обнуление переменой Time
} Виконується за викликом таймера з частотою приблизно 25 кГц, головний косяк мікроконтролер працює на 8 МГц, а цей код зжера десь 40% ресурсів контролера, як побутися одного оператора if придумав, але виграш буде не великий ![]()
Критичний по часу лишек цей шматок.
Повний ний скетчь тут:
// https://www.youtube.com/watch?v=ACVtKDJVXS4 https://www.youtube.com/watch?v=p0v2vSmajx8&t=1s https://www.youtube.com/watch?v=8xGrPpXyc5w
// ЭТО СКЕТЧ ПРИЁМНИКА!!!
//--------------------- НАСТРОЙКИ ------------------------------
#define CH_NUM 0x60 // номер канала (должен совпадать с передатчиком)
#define SIG_POWER RF24_PA_MIN // УРОВЕНЬ МОЩНОСТИ ПЕРЕДАТЧИКА
// На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
#define SIG_SPEED RF24_1MBPS // ---------------------СКОРОСТЬ ОБМЕНА--------------------------
// На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
// должна быть одинакова на приёмнике и передатчике!
// при самой низкой скорости имеем самую высокую чувствительность и дальность!!
// ВНИМАНИЕ!!! enableAckPayload НЕ РАБОТАЕТ НА СКОРОСТИ 250 kbps!
//------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <avr/io.h> // Даная часть взята здесь https://habr.com/ru/articles/453276/
#include <avr/interrupt.h>
//------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
RF24 radio(9, 10); // Задание выводов к которым подключен модуль Wi-Fi + 11, 12, 13(аппаратный интерфейс)
//--------------------- ПЕРЕМЕННЫЕ для работы с nRF24L01----------------------
byte pipeNo;
byte address[][6] = {"1Node", "2Node", "3Node", "4Node", "5Node", "6Node"}; // возможные номера труб
int recieved_data[16]; // Массив принятых данных
int telemetry[2]; // Массив данных телеметрии (то что шлём на передатчик)
// !!! Больше 2 ячеек масив делать нельзя - перестает работать!!!
// Предположительно ограничение на длину "обратной посылки"
// Переменные для промежуточных результатов, чтобы не менять масив с приёмника, они будут и для PPM, и для ШИМ, и для дискретных выходов
// Названия выбраны чтобы сразу была асоцияция с ногой микроконтролера
// Выводы 0 и 1 оставлены для СОМ, вывод 9 использует nRF24L01
byte PD_2; // Дискретный, PWM
byte PD_3; // Дискретный, PWM, ШИМ
byte PD_4; // Дискретный, PWM
byte PD_5; // Дискретный, PWM, ШИМ
byte PD_6; // Дискретный, PWM, ШИМ
byte PD_7; // Дискретный, PWM
byte PB_0; // Дискретный, PWM
// Поскольку стандартная библиотека Servo на 8 МГц работала ХЕРОВО пришлось написать свой аналог, работает на прерываниях,
// использует Timer1, что делает недоступным ШИМ на выводах 9 и 10(они все равно заняты nRF24L01)
volatile long Time; // Переменая для формирования временых отрезков, "volatile" - изменяется в прерывании
byte T_min = 20; // Служебная переменая - для того чтобы регулировать минимальную длительность импулься
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
void setup() {
//Serial.begin(9600); // Настройка СОМ порта
//---------------------------------------------Настройка радиомодуля, в оригинале какого то х... были выполнены в виде подпрограммы----------------------------
radio.begin(); // активировать модуль
radio.setAutoAck(1); // режим подтверждения приёма, 1 вкл 0 выкл
radio.setRetries(0, 15); // (время между попыткой достучаться, число попыток)
radio.enableAckPayload(); // разрешить отсылку данных в ответ на входящий сигнал
radio.setPayloadSize(32); // размер пакета, байт
radio.openReadingPipe(1, address[0]); // хотим слушать трубу 0
radio.setChannel(CH_NUM); // выбираем канал (в котором нет шумов!)
radio.setPALevel(SIG_POWER); // уровень мощности передатчика
radio.setDataRate(SIG_SPEED); // скорость обмена
// должна быть одинакова на приёмнике и передатчике!
// при самой низкой скорости имеем самую высокую чувствительность и дальность!!
radio.powerUp(); // начать работу
radio.startListening(); // начинаем слушать эфир, мы приёмный модуль
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
// Какой вывод для чего будет использоватся будет определенно отдельно в програме
pinMode (2, OUTPUT); // Дискретный, PPM
pinMode (3, OUTPUT); // Дискретный, PPM, ШИМ
pinMode (4, OUTPUT); // Дискретный, PPM
pinMode (5, OUTPUT); // Дискретный, PPM, ШИМ
pinMode (6, OUTPUT); // Дискретный, PPM, ШИМ
pinMode (7, OUTPUT); // Дискретный, PPM
pinMode (8, OUTPUT); // Дискретный, PPM
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
// Инициализация Timer1, кусок взят от сюда https://habr.com/ru/articles/453276/
// Взят без изменений, работает и ладно, кроме настроек делителя и регистра совпадений чтобы получить нужную частоту прерываний ничего не нужно
// План такой: генерируется прерывание, переменя Time увеличивается на подобраное число, вивод/выводы сигнала PPM устанавливаются в 1 и оставляются
// так 500 мкс для формирования минимальной длины импулься, после этого проверяется "а не пора ли установить на выводе ноль" сравнивая с переменой Serv
// Таймер 1 используется поскольку выводы 9 и 10 все равно задействованы для управления Wi-Fi модулем.
// Редакция 02, усложненая, добавляется еще 4 вывода 14, 15, 16, 17(А0, А1, А2, А3), выводы 5 и 6 умышленно не трогаю - на них возможен аппаратный ШИМ
// выводы 4, 7, 8 оставлены для дискретных сигналов. Выводы 0 и 1 оставлены пустыми для СОМ порта.
cli(); // отключить глобальные прерывания
TCCR1A = 0; // установить регистры в 0
TCCR1B = 0;
// Задание частоты прерываний выполняется програмированием регистров OCR1A, TCCR1B CS10, TCCR1B CS11,TCCR1B CS12
// CS12=0, CS11=0, CS10=1 делитель 0
// CS12=0, CS11=1, CS10=0 делитель 8
// CS12=0, CS11=1, CS10=1 делитель 64
// CS12=1, CS11=0, CS10=0 делитель 256
// CS12=1, CS11=0, CS10=1 делитель 1024
// Для 1000 прерываний в секунду CS12=0, CS11=0, CS10=1, OCR1A = 319, на выходе импульсы с частотой почти 50 Гц
// Неприятный сюрприз - 8 МГц ардуинка 1000 прерываний не тянет :(
// Пришлось уменьшить число временых интервалов с 1000 до 500, а число "уровней" PPM сигнала до 32 :(
// От отправки даных в СОМ порт вообще пришлось отказатся :(
OCR1A = 319; // установка регистра совпадения
TCCR1B |= (1 << WGM12); // включить CTC режим
TCCR1B |= (1 << CS10); // Установить биты на коэффициент деления
TCCR1B |= (0 << CS11);
TCCR1B |= (0 << CS12);
TIMSK1 |= (1 << OCIE1A); // включить прерывание по совпадению таймера
sei(); // включить глобальные прерывания
//------------------------------------------------------------------------------------------------------------------------------------------------------------
delay (100); // Задержка на всякий случай, скорее всего нужно убрать
}
//--------------------------------------------------------------Основной цикл---------------------------------------------------------------------------------
void loop() {
// Приём пакета с пульта управления и отправка телеметрии
while (radio.available(&pipeNo)) { // слушаем эфир
radio.read(&recieved_data, sizeof(recieved_data)); // чиатем входящий сигнал
telemetry[0] = analogRead(A5); // формируем пакет данных телеметрии (напряжение АКБ, скорость, температура...)
telemetry[1] = 20; // для пробы меряем собственное питание, второе значение с потолка
radio.writeAckPayload(pipeNo, &telemetry, sizeof(telemetry)); // отправляем пакет телеметрии
}
//Serial.print(" А0: "); Serial.print(recieved_data[0]); // Отправка масива с АЦП в СОМ порт, для отладки
//Serial.print(" А1: "); Serial.print(recieved_data[1]);
//Serial.print(" А2: "); Serial.print(recieved_data[2]);
//Serial.print(" А3: "); Serial.print(recieved_data[3]);
//Serial.print(" А4: "); Serial.print(recieved_data[4]);
//Serial.print(" А5: "); Serial.print(recieved_data[5]);
//Serial.print(" Pin 7: "); Serial.println(recieved_data[6]); // Перенос строки
//----------------------Проба вывода управляющих сигналов------------------------------------------------------------------------------------------------------
digitalWrite(3,recieved_data[6]); // Управление цифровым пином 3, даные для теста в 6 ячейке масива !!!!! Уточнить ячейку масива !!!!!
PD_5 = map(recieved_data[2],0,1023,0,255); // Преообразование из 0...1023 выдаваемого АЦП в 0...255 принимаемого ШИМ
analogWrite(5,PD_5); // Запись в регистр отвечающий за ШИМ, конкретно здесь исползуется вывод 5
PD_6 = map(recieved_data[3],0,1023,0,255); // Преообразование из 0...1023 выдаваемого АЦП в 0...255 принимаемого ШИМ
analogWrite(6,PD_6); // Запись в регистр отвечающий за ШИМ, конкретно здесь исползуется вывод 6
// Тут только присвоение значения сигнела PPM и добавление T_min для формирования правильной длины импульса, само формирование PPM выполняэтся в прерываниях
PD_2 = T_min + map(recieved_data[0],0,1023,0,31); // Вывод 2 будет использован для вывода сигнела PPM из 0 ячейки масива
PD_4 = T_min + map(recieved_data[1],0,1023,0,31); // Вывод 4 будет использован для вывода сигнела PPM из 1 ячейки масива
PD_7 = T_min + map(recieved_data[2],0,1023,0,31); // Вывод 7 будет использован для вывода сигнела PPM из 2 ячейки масива
PB_0 = T_min + map(recieved_data[3],0,1023,0,31); // Вывод 8 будет использован для вывода сигнела PPM из 3 ячейки масива
}
//--------------------------------------------------------------Основной цикл закончен------------------------------------------------------------------------
//______________________________________________________________Прерывание и действия в нем___________________________________________________________________
ISR(TIMER1_COMPA_vect)
{
Time = Time+1;
if (Time==1) {
// Высокий уровень выставляется на всех выходах одновременно
digitalWrite(2, HIGH); // Установка на выводе 2 высокого уровня, начало формирования импульса
digitalWrite(4, HIGH); // Установка на выводе 4 высокого уровня, начало формирования импульса
digitalWrite(7, HIGH); // Установка на выводе 7 высокого уровня, начало формирования импульса
digitalWrite(8, HIGH); // Установка на выводе 8 высокого уровня, начало формирования импульса
}
// Низкий уровень выставляется для каждого вывода индивидуально в зависимости от величины управляющего сигнала
if (Time==PD_2) {digitalWrite(2, LOW);} // Установка на выводе 2 низкого уровня, конец формирования импульса
if (Time==PD_4) {digitalWrite(4, LOW);} // Установка на выводе 4 низкого уровня, конец формирования импульса
if (Time==PD_7) {digitalWrite(7, LOW);} // Установка на выводе 7 низкого уровня, конец формирования импульса
if (Time==PB_0) {digitalWrite(8, LOW);} // Установка на выводе 8 низкого уровня, конец формирования импульса
if (Time==500) {
Time = 0; // Формирование паузы и обнуление переменой Time
}
}
//____________________________________________________________Конец прерывания_________________________________________________________________________________Може хто зна підкажіть:
Є Arduino Pro 8 МГц 3,3В в системі радіокерування, все вже пращює, АЛЕ з PWM сигналами - як завжди знайшлися ГРАБЛІ...
Городити щось додатково недоцільно, задумка у простоті і можливості міксувати канали керування
Сигнал "брудний" - на екрані осцилографа імпульси керування смикаються у часі, тупі китайскі серви погоджуються і на такий, а от драйвер BLDC двигуна вже посила на три літери...

По центру імпульс нормального сигналу, лівіше хибний імпульс(він по екрану біга). Скетч не додаю, я вже вирізав з основного циклу все - мікроконтролер не робе нічого окрім генерації PWM сигналу, а проблема є!!!
Библіотека Servo8Bit з ATMega32 наче не працюэ і з менеджера бібліотек не встановлюється, є ідеї як змусити Arduino Pro нормально генерувати PWM сигнал?
Це аналог NEMA17, які до речі бувають ДУЖЕ різні.
Для крокового двигуна напруга вториний параметр, головне струм, момент і кількість кроків на оберт.
Драйвер 4833 регулює струм, максимум що вам загрожує при використані таких двигунів - принтер не вдасться розігнати вище 45-60 мм/сек, але це малоймовірно, дуже малоймовірно.
99% лише коли потрібна максимальна швидкість виконаня програми, але майже ЗАВЖДИ ці функції одні з найшвидших у програмі і пряме курування через регистри мало що дасть...
Сторінки 1