Как настроить выводы ввода-вывода второго квадратурного декодера в Arduino IDE
Я работаю над считыванием данных с двух квадратурных энкодеров с помощью одного Arduino DUE в рамках своего проекта, связанного с летними каникулами. По следующей ссылке http://atmel.force.com/support/servlet/fileField?id=0BEG0000000HRvP, SAM3X8E имеет 2 квадратурных декодера. Теперь я не могу понять, как я могу инициализировать этот второй квадратурный декодер. В соответствии с квадратурным декодером QDEC для SAM3/4 Devices_ApplicationNote_AT11483 Pg16 мы должны сначала настроить IOPINS для QDEC1. Мне нужно выполнить ту же задачу в Arduino IDE вместо Atmel Studio.
Это код, который я нашел, и он хорошо работает для чтения кодировщика с использованием QDEC0. Однако теперь мне нужно прочитать энкодер, используя второй квадратурный декодер, чьи входные контакты TIOA6 и TIOB6 соответствуют контактам 5 и 4 платы Arduino DUE. После прочтения таблицы данных Atmel SAM3X я думаю, что мне следует заменить «TC0» на «TC2», чтобы второй декодер заработал. Однако это не работает. Я думаю, что это не работает, потому что канал 2 занят вычислениями временной базы. В примечаниях по применению 5.52 пункт 2 Режим измерения скорости QDEC говорит об инициализации TC0, TC1, TC2. Если бы я хотел использовать другой кодировщик, экземпляр таймера которого будет использоваться.
/*
** Quadrature Encoder on Arduino Due in "Speed Mode"
** Will not work on other Arduino types because of a specific hardware requirement.
**
** @bungle99 / 2014-02-28
**
** Many thanks to @Designservicecorp (notably post #32 and post #42) that gave me the base to work from.
**
** Uses *hardware* to do the heavy lifting of interpreting the Encoder output, which gives it significant
** advantage over other Arduino's for this use case. Eg many people have reported their Unos maxing out
** or skipping counts when interpretting high RPMs, whereas people using the Due in hardware mode have
** reported better success with high RPMs.
**
** This particular example puts the hardware into "Speed" mode, whereas other examples in this thread use
** "Position" mode (see posts 32, 42). The one example in the thread that attempts Speed calcs uses Position
** Mode and uses an interrupt on the Z Index axis to manually apply a time period (needed to caculate speed)
** on each rotation.
**
** This code below has been used in tests (using Lego NXT and a gear train for driving / comparison) to
** ~3,500 RPM with a 1,024 PPR Encoder (4 x 1,024 Edges). The Arduino didn't seem to struggle and the
** numbers (eg RPM) matched fairly closely with the Lego (eg as a sanity check, the calcs in the code look
** correct).
**
** I bought one of these encoders, which seems ok;
**
** * http://proto-pic.co.uk/rotary-encoder-1024-pr-quadrature/
**
** Great service from proto-pic. The encoder specs are officially
**
** * http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Robotics/E6B2Encoders.pdf
**
** But look an awful lot like this one too (which is easier to read)
**
** * http://www.ia.omron.com/product/item/e6b27032r/index.html
**
** Note that this Encoder is rated at 5V min. I've successully powered it off the 5V Arduino pin header,
** and it doesn't work off the 3.3V one. At 5V, the outputs need to be run through some resistors to bring
** them into tolerance of the Arduino inputs.
**
** The inputs are 2 and 13 (and optionally A6 for the index).
**
** The test hardware (some Lego NXT servos and gear train) was unable to turn any faster (more gears
** stalled the servos) - eg the Arduino Due was not the limiting factor. At this speed, the Due Hardware
** is dealing with approx 238,000 edges per second.
**
** This example offloads the speed calculation onto the hardware, meaning no interrupt is required.
** In addition, the index is not required either, so it allows speeds to be seen even for rotations that
** don't pass the index marker (for example, slow rotations needing a speed before a complete rotation).
** That said, index is available if you want it and the docs suggest you can still use it (and others) as
** interrupt if you wish.
**
** NB There appears to be a myriad of features on the Chip that have not been touched. Eg you can still add
** interrupts to this code to do other things; eg passing the Z index, direction changes. There are also
** filters available if your encoder suffers from noise (eg vibrations). All this is available to play with
** another day :)
**
** Code appears to work, but no guarantees. Understand it. Test it. Feedback to the group. YMMV. Don't blame
** me if it doesn't work or expolodes!
*/
const int ENCODER_EDGES_PER_ROTATION = 3200; // это зависит от вашего энкодера
const int ENCODER_SAMPLES_PER_SECOND = 10; // это нужно будет настроить в зависимости от вашего варианта использования...
const int LOOP_DELAY_MS = 1 * 1000; // ... как и это (см. комментарии в основном коде)
void setup() {
Serial.begin(115200);
delay(10);
// Настройка квадратурного энкодера
// http://www.atmel.com/Images/doc11057.pdf
// Раздел 37 p869
// Раздел 37.6.14 p885
// Раздел 37.6.14.2 p890 Измерение положения и вращения
// Раздел 37.6.14.5 p891 Измерение скорости (о чем речь)
REG_PMC_PCER0 = PMC_PCER0_PID27
| PMC_PCER0_PID28
| PMC_PCER0_PID29;
// Настройка канала в режиме сигнала (например, ввод в кодировщик для запуска выборки на основе времени)
// Обратите внимание, что некоторые варианты здесь влияют на расчеты ниже, и если вы их измените, вам нужно изменить
// следующий раздел по размеру.
// Раздел 37.7.11 p906 (также обратитесь к Разделу 37.6.14.5 p891, чтобы узнать, что нужно установить)
REG_TC0_CMR2 = TC_CMR_TCCLKS_TIMER_CLOCK4
| TC_CMR_WAVE
| TC_CMR_ACPC_TOGGLE
| TC_CMR_WAVSEL_UP_RC;
// Теперь определяем период выборки, взяв за основу часы, выбранные выше
// Обратите внимание, что REG_TC0_CMR2 выше использует CLOCK4; это делитель 128. Вам нужно изменить
// делитель ниже, если вы измените часы выше. Вы можете изменить входные часы и режим RC на
// подходит для вашего приложения (например, сколько импульсов вы ожидаете - зависит от типа энкодера
// и самая медленная/нормальная/самая высокая скорость вращения и что вы хотите сделать с результатом).
// Раздел 37.6.14.5 p891 отмечает, что вам нужно настроить это, иначе все время будет 0 :-)
REG_TC0_RC2 = F_CPU / 128 / ENCODER_SAMPLES_PER_SECOND;
// Настройка канала в режиме захвата
// Раздел 37.7.10 p904 (также обратитесь к Разделу 37.6.14.5 p891 для получения подробной информации о том, что нужно установить)
REG_TC0_CMR0 = TC_CMR_ABETRG
| TC_CMR_LDRA_EDGE
| TC_CMR_LDRB_EDGE
| TC_CMR_ETRGEDG_EDGE
| TC_CMR_CPCTRG;
// Включить функции, отметив, что выбрана скорость, а не позиция
// Раздел 37.7.2 p895 (также см. Раздел 37.6.14.5 p891 для получения подробной информации о том, что устанавливать)
// 37.7.2 Регистр блочного режима TC
REG_TC0_BMR = TC_BMR_QDEN
| TC_BMR_SPEEDEN
| TC_BMR_EDGPHA; //стр. 896
/*EDGPHA: EDGe on PHA count mode
0: edges are detected on both PHA and PHB.
1: edges are detected on PHA only.*/
// Запускаем все
REG_TC0_CCR0 = TC_CCR_CLKEN | TC_CCR_SWTRG;
REG_TC0_CCR1 = TC_CCR_CLKEN | TC_CCR_SWTRG;
REG_TC0_CCR2 = TC_CCR_CLKEN | TC_CCR_SWTRG;
}
void loop() {
int iIndexCount = REG_TC0_CV1; // Это не нужно, но в руководстве указано, что это доступно
int iSpeedPPP = REG_TC0_RA0; // Это то, что нам действительно нужно (скорость в импульсах на период выборки)
// который мы можем легко преобразовать в rps или rpm
double dSpeedRPS = ((iSpeedPPP / (ENCODER_EDGES_PER_ROTATION * 1.0)) * ENCODER_SAMPLES_PER_SECOND);
double dSpeedRPM = dSpeedRPS * 60;
Serial.print("Speed ppp: ");
Serial.print(iSpeedPPP);
Serial.print(", ");
Serial.print("Speed rps: ");
Serial.print(dSpeedRPS);
Serial.print(", ");
Serial.print("Speed rpm: ");
Serial.print(dSpeedRPM);
Serial.print(", ");
Serial.print("Indexes: ");
Serial.print(iIndexCount);
Serial.print(". ");
Serial.println();
// Замедлить основной цикл, учитывая, что кодировщик может обновляться независимо в фоновом режиме (мы
// эффективно опрашивая его, чтобы узнать самую последнюю и лучшую информацию о каждом цикле).
delay(LOOP_DELAY_MS);
}
Дополнительно я хотел спросить, в чем разница между REG_TC0_CMR2, REG_TC0_CMR0 и REG_TC0_CMR1? Два из них используются в приведенном выше коде.
Двигатель с энкодером, который я использую:
@Masood Salik, 👍1
1 ответ
Лучший ответ:
Блоки TC практически идентичны (за исключением того, что в TC1 не подключены некоторые сигналы к контактам)
Поэтому вам нужно скопировать регистры, содержащие *_TC0_* =
, и заменить TC0 на TC2.
Плюс PC25 и PC24 должны быть установлены на его альтернативную функцию PIO_PERIPH_B. Что-то вроде этого:
PIO_Configure(PIOC, PIO_PERIPH_B, PIO_PC25B_TIOA6, PIO_DEFAULT);
PIO_Configure(PIOC, PIO_PERIPH_B, PIO_PC26B_TIOB6, PIO_DEFAULT);
Кроме того, часы TC6, TC7 и TC8 должны быть включены:
REG_PMC_PCER1 = PMC_PCER1_PID33 | PMC_PCER1_PID34 | PMC_PCER1_PID35;
И TC_CMR0..2 являются регистрами режима канала для одного из трех таймеров/счетчиков в одном блоке TC. Опять же, это то же самое, но зависит от режима WAVE.
Полная конфигурация:
REG_PMC_PCER0 = PMC_PCER0_PID27 | PMC_PCER0_PID28 | PMC_PCER0_PID29;
REG_PMC_PCER1 = PMC_PCER1_PID33 | PMC_PCER1_PID34 | PMC_PCER1_PID35;
PIO_Configure(PIOB, PIO_PERIPH_B, PIO_PB25B_TIOA0, PIO_DEFAULT); // ардуино контакт 2
PIO_Configure(PIOB, PIO_PERIPH_B, PIO_PB27B_TIOB0, PIO_DEFAULT); // ардуино контакт 13
REG_TC0_CMR2 = TC_CMR_TCCLKS_TIMER_CLOCK4 | TC_CMR_WAVE | TC_CMR_ACPC_TOGGLE | TC_CMR_WAVSEL_UP_RC;
REG_TC0_RC2 = F_CPU / 128 / 1;
REG_TC0_CMR0 = TC_CMR_ABETRG | TC_CMR_LDRA_EDGE | TC_CMR_LDRB_EDGE | TC_CMR_ETRGEDG_EDGE | TC_CMR_CPCTRG;
REG_TC0_BMR = TC_BMR_QDEN | TC_BMR_SPEEDEN /*| TC_BMR_EDGPHA*/;
REG_TC0_CCR0 = TC_CCR_CLKEN | TC_CCR_SWTRG;
REG_TC0_CCR1 = TC_CCR_CLKEN | TC_CCR_SWTRG;
REG_TC0_CCR2 = TC_CCR_CLKEN | TC_CCR_SWTRG;
PIO_Configure(PIOC, PIO_PERIPH_B, PIO_PC25B_TIOA6, PIO_DEFAULT); // Arduino контакт 5
PIO_Configure(PIOC, PIO_PERIPH_B, PIO_PC26B_TIOB6, PIO_DEFAULT); // Arduino контакт 4
REG_TC2_CMR2 = TC_CMR_TCCLKS_TIMER_CLOCK4 | TC_CMR_WAVE | TC_CMR_ACPC_TOGGLE | TC_CMR_WAVSEL_UP_RC;
REG_TC2_RC2 = F_CPU / 128 / 10;
REG_TC2_CMR0 = TC_CMR_ABETRG | TC_CMR_LDRA_EDGE | TC_CMR_LDRB_EDGE | TC_CMR_ETRGEDG_EDGE | TC_CMR_CPCTRG;
REG_TC2_BMR = TC_BMR_QDEN | TC_BMR_POSEN /*| TC_BMR_EDGPHA*/;
REG_TC2_CCR0 = TC_CCR_CLKEN | TC_CCR_SWTRG;
REG_TC2_CCR1 = TC_CCR_CLKEN | TC_CCR_SWTRG;
REG_TC2_CCR2 = TC_CCR_CLKEN | TC_CCR_SWTRG;
- Чтение двух квадратурных кодировщиков с помощью одного ардуино
- Разные и самые быстрые способы вычисления синусов и косинусов в Arduino
- Ручная установка Arduino Due
- Эквивалент millis() в студии Atmel
- Arduino Uno: avrdude: stk500_recv(): программатор не отвечает
- Как использовать arduino IDE для компиляции файлов .s
- Пример UDPSendReceiveString с использованием Arduino Due с Ethernet-модулем ENC28J60
- Atmel Studio 7 или Arduino IDE для кодирования на уровне регистров?
Я пробовал это, но это не работает. Мы не получаем никакого измерения...., @Masood Salik
@MasoodSalik также должны быть включены часы для каналов 6-8. Хотя я включил его для всего блока с помощью
REG_PMC_PCER0 = PMC_PCER_PID29
, но это был всего лишь третий счетчик в TC0., @KIIVЯ включил все часы, используя
{ REG_PMC_PCER0 = PMC_PCER0_PID27 | PMC_PCER0_PID28 | PMC_PCER0_PID29 | PMC_PCER0_PID30 | PMC_PCER0_PID31; REG_PMC_PCER1 = PMC_PCER1_PID32 | PMC_PCER1_PID33 | PMC_PCER1_PID34 | PMC_PCER1_PID35; }
Теперь я могу читать QDEN1 с контактов 4 и 5. QDEN0 с контактов 2 и 13. Сейчас я считываю число кликов за оборот, используя REG_TC2_CV0 и REG_TC0_CV0. Теперь я пытаюсь измерить скорость, @Masood Salik