Используйте Arduino MEGA для генерации нескольких прямоугольных сигналов частотой 40 кГц с 10 фазами.
Я хочу использовать Arduino-mega для генерации нескольких прямоугольных сигналов с частотой 40 кГц, и его можно использовать для управления ультразвуковым преобразователем.
Итак, я использую стратегию из статьи "Ultraino: открытая система с фазированной решеткой для Узкополосная передача ультразвука по воздуху», которая применяет 10-битный сигнал с последовательностью «0» или «1» в PORTA, PORTC... и т. д. для получения прямоугольного сигнала, как на рис. 1.
Рис. 1 прямоугольный сигнал
Затем я меняю шаблон последовательности на сдвиг фазы, и всего 10 фаз, возникает какая-то проблема.
Мне любопытен принцип этого метода:
Почему последовательность может генерировать сигнал с частотой 40 кГц? Означает ли это, что Arduino будет тратить несколько микросекунд на выполнение команды типа
POTRA=0x1
. Я пытаюсь изменить длину массива, он больше не будет хорошо генерировать прямоугольную волну.Когда я выполняю функцию сдвига
shiftPhase
, она имеет 10 фаз. В 5 фазах она будет перемещать прямоугольную волну, как на рис. 2, остальные 5 фаз будут как на рис. 3, обязанность прямоугольной волны изменяется, как и инвертированная. Итак, как это происходит, просто с изменением последовательности 0,1?
Рис. 2. Успех волны сдвига
Рис.3 волна сдвига не удалась
код, который я использую:
#include <avr/sleep.h>
#include <avr/power.h>
#define N_PATTERNS 1
#define N_PORTS 10
#define N_DIVS 10
#define COMMAND_SWITCH 0b00000000
#define COMMAND_DURATION 0b00110000
#define MASK_DURATION 0b00111111
#define COMMAND_COMMITDURATIONS 0b00010000
#define WAIT(a) __asm__ __volatile__ ("nop")
#define OUTPUT_WAVE(pointer, d) PORTA = pointer[d*N_PORTS + 0]; PORTC = pointer[d*N_PORTS + 1]; PORTL = pointer[d*N_PORTS + 2]; PORTB = pointer[d*N_PORTS + 3]; PORTK = pointer[d*N_PORTS + 4]; PORTF = pointer[d*N_PORTS + 5]; PORTH = pointer[d*N_PORTS + 6]; PORTD = pointer[d*N_PORTS + 7]; PORTG = pointer[d*N_PORTS + 8]; PORTJ = pointer[d*N_PORTS + 9]
static byte bufferA[N_PATTERNS * N_DIVS * N_PORTS];
static byte bufferB[N_PATTERNS * N_DIVS * N_PORTS];
static byte animation[100] = {
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0xff,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0xff,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0xff,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0xff,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0xff,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
};
String str="";
void shiftPhase(byte* p,int len,int inter,int stepsize,byte id)
{
byte mask=0xff-id;
byte q[10]={0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0};
for(int i=0;i<10;i++)
{
q[i]=p[i*inter];
}
for(int i=0;i<10;i++)
{
p[i*inter]=(q[(i+stepsize+10)%10]&id)|(mask&q[i]);
}
}
void setup()
{
//устанавливаем в качестве выходных портов ACLBKFHDGJ
DDRA = DDRC = DDRL = DDRB = DDRK = DDRF = DDRH = DDRD = DDRG = DDRJ = 0xFF;
//слабый сигнал на всех из них
PORTA = PORTC = PORTL = PORTB = PORTK = PORTF = PORTH = PORTD = PORTG = PORTJ = 0x00;
//очистить буферы
for (int i = 0; i < (N_PATTERNS * N_DIVS * N_PORTS); ++i) {
bufferA[i] = bufferB[i] = 0;
}
for (int i = 0; i < 100; ++i)
{
bufferA[i] = animation[i];
}
// отключаем все что нам не нужно
ADCSRA = 0; // АЦП
power_adc_disable ();
power_spi_disable();
power_twi_disable();
power_timer0_disable();
power_usart1_disable();
power_usart2_disable();
Serial.begin(9600);
byte bReceived = 0;
bool byteReady = false;
bool emittingA = true;
byte* emittingPointerH = & bufferA[0];
byte* emittingPointerL = & bufferA[N_PORTS * N_DIVS / 2];
LOOP:
OUTPUT_WAVE(emittingPointerH, 0);
OUTPUT_WAVE(emittingPointerH, 1);
OUTPUT_WAVE(emittingPointerH, 2);
OUTPUT_WAVE(emittingPointerH, 3);
OUTPUT_WAVE(emittingPointerH, 4);
OUTPUT_WAVE(emittingPointerL, 0);
OUTPUT_WAVE(emittingPointerL, 1);
OUTPUT_WAVE(emittingPointerL, 2);
OUTPUT_WAVE(emittingPointerL, 3);
OUTPUT_WAVE(emittingPointerL, 4);
byteReady = Serial.available();
if(byteReady!=0)
{
str="";
str=char(Serial.read());
Serial.println(str);
if(str=="1")
{
shiftPhase(bufferA,10,10,1,0x1);
}
if(str=="2")
{
shiftPhase(bufferA,10,10,-1,0x1);
}
}
while(Serial.read()>=0){}
goto LOOP;
}
void loop() {}
Большое спасибо!
Код статьи можно увидеть по ссылке.
@Shiny100, 👍0
Обсуждение1 ответ
Лучший ответ:
Shiny100 спросил:
Означает ли это, что Arduino будет тратить несколько микросекунд на выполнение команды? например
POTRA=0x1
.
Я попытался скомпилировать POTRA=0x1
и получил следующее:
ldi r24, 0x01 ; load 0x1 into register r24
out 0x02, r24 ; output register r24 to PORTA (I/O address 0x02)
Эти инструкции выполняются за один цикл, поэтому последовательность занимает
2 цикла, т. е. 0,125 мкс при 16 МГц. Ваш код, однако,
делает намного больше, чем это. Макрос OUTPUT_WAVE()
включает чтение
десять значений из ОЗУ и запись их на десять разных портов. Первое
вызов этого макроса компилируется в это:
lds r24, 0x028D ; r24 = bufferA[0]
out 0x02, r24 ; PORTA = r24
lds r24, 0x028E ; r24 = bufferA[1]
out 0x08, r24 ; PORTC = r24
lds r24, 0x028F ; r24 = bufferA[2]
sts 0x010B, r24 ; PORTL = r24
lds r24, 0x0290 ; r24 = bufferA[3]
out 0x05, r24 ; PORTB = r24
lds r24, 0x0291 ; r24 = bufferA[4]
sts 0x0108, r24 ; PORTK = r24
lds r24, 0x0292 ; r24 = bufferA[5]
out 0x11, r24 ; PORTF = r24
lds r24, 0x0293 ; r24 = bufferA[6]
sts 0x0102, r24 ; PORTH = r24
lds r24, 0x0294 ; r24 = bufferA[7]
out 0x0b, r24 ; PORTD = r24
lds r24, 0x0295 ; r24 = bufferA[8]
out 0x14, r24 ; PORTG = r24
lds r24, 0x0296 ; r24 = bufferA[9]
sts 0x0105, r24 ; PORTJ = r24
Инструкция out
по-прежнему однотактная, но lds
(загрузка из
память) и sts
(сохранение в памяти) занимают два цикла каждый. Вы можете заметить
что только порты от A до F доступны с помощью инструкции out
.
Это связано с тем, что они отображаются в адресном пространстве ввода-вывода
микроконтроллер. Доступ к другим четырем портам осуществляется с использованием более медленного
Инструкция sts
для отображенных в памяти адресов.
Если вы подсчитаете свои циклы, вы должны получить 34 цикла для всего
последовательность, которая составляет 2,125 мкс на частоте 16 МГц. В идеале вы хотели бы
это займет 2,5 мкс (1/10 цикла сигнала 40 кГц). Ты
Вы можете заметить, что код, из которого вы черпали вдохновение, имеет несколько
инструкции между последовательными вызовами OUTPUT_WAVE()
. мне кажется
как будто они были тщательно рассчитаны, чтобы подобраться как можно ближе к
2,5 мкс на фазу. Такой подход явно хрупок: любой крошечный
изменения в коде или даже обновление компилятора могут сбить вас с толку.
тщательно рассчитанный по времени код.
Обновление: второй вопрос, если я правильно понял, касается
изменение рабочего цикла. Сигналы имеют номинальный рабочий цикл
50%: сигнал НИЗКИЙ в течение 5 временных интервалов (5/10 цикла), затем
HIGH для следующих 5 временных интервалов. Однако временные интервалы не
имеют все одинаковую длину. Первые девять длятся по 2,125 мкс каждый.
последний, однако, несколько длиннее, потому что занимает продолжительность
код, обрабатывающий последовательный порт, и два цикла goto LOOP;
.
Если более длинный временной интервал происходит, когда выход ВЫСОКИЙ, вы получаете
фактический рабочий цикл выше 50%. Если это происходит, когда оно ВЫСОКОЕ, вы
получить более короткий рабочий цикл, как в вашем примере «сбой волны сдвига».
Спасибо, я думаю, что понял это более четко. Но мне интересно, как я могу получить информацию о компиляции, как вы, и могу ли я создать более 24 прямоугольных волн, как [это] (https://runtimemicro.com/Forums/RTM_TimerCalc /Примеры-и-Советы/Arduino-Timer-Phase-Shifted-Square-Waves) метод?, @Shiny100
@ Shiny100: Вы можете разобрать файл .elf с помощью команды avr-objdump -S
. avr-objdump уже должен быть где-то в вашей установке Arduino. Метод, на который вы ссылаетесь, может управлять до трех сигналов на таймер. Используя функцию сброса предварительного делителя таймеров 0, 1, 3, 4 и 5, вы можете синхронизировать эти таймеры и когерентно управлять до 13 сигналов., @Edgar Bonet
- Как разделить входящую строку?
- Как использовать SPI на Arduino?
- Как сбросить или отформатировать Arduino?
- Управление скоростью вентилятора с помощью библиотеки Arduino PID
- Arduino Due vs Mega 2560
- Как получить уникальный идентификатор для всех плат Arduino?
- Почему я получаю avrdude: stk500v2_ReceiveMessage(): timeout error when uploading to Arduino Mega?
- Тайм-аут связи Arduino Mega с ошибкой программатора
Не по теме: `PORTA = PORTB = ... = PORTG = ... = 0xFF;' действительно плохая идея. PORTG имеет только 6 бит, и, согласно техническому описанию, неиспользуемые контакты читаются как 0. И этот шаблон присваивается самому правому порту, затем считывает значение и назначает его следующему., @KIIV