Конструкция выходного дня: простой спектранализатор 480 полос 60 дБ на stm32f103 и ILI9481
- Войдите на сайт для отправки комментариев
Собственно возникла идея собрать спектранализатор звуковой частоты на популярном дисплейчике 320х480.
Т.к. с stm32 знаком меньше месяца, особо задействовать его возможности не стал: все делается программно и последовательно, в том числе и частота дискретизации 48 кГц (точнее, 47.6 кГц - период 21 мкс).
FFT выполняется в плавающей точке, но т.к. занимает около 60 мс, т.е. порядка половины всего последовательного цикла, я решил пока ничего не делать.
Кстати, частота обновления экрана составляет примерно 8.5 Гц.
#define LCD_RD 0x8000 // PB15 - не используется (если использовать, нужно согласовать уровни сигналов)
#define LCD_WR 0x4000 // PB14
#define LCD_RS 0x2000 // PB13
#define LCD_CS 0x1000 // PB12
#define LCD_RESET 0x0800 // PB11
#define DATA_MASK 0x07f8 // PB3-PB10
#define CTRL_MASK_DEFAULT 0xf800 // биты PB0-PB2 не используется, поэтому не 0xf107: все управляющие биты HIGH
#define CTRL_MASK_WR_RS_SC 0x8803 // маска для записи команд
#define CTRL_MASK_WR_SC 0xa803 // маска для записи данных
#define AUDIO_INPUT PA0 // аналоговый вход
void Lcd_Write_Com(unsigned char d) // вывод команды: опустить PB13, вывести байт, поднять PB13
{
GPIOB_BASE->ODR = CTRL_MASK_WR_RS_SC | (d << 3);
GPIOB_BASE->BSRR = LCD_WR;
}
inline void Lcd_Write_Data(unsigned char d)
{
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | (d << 3);
GPIOB_BASE->BSRR = LCD_WR;
}
inline void Address_set(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2) // позиционирование на экране (оптимизировано)
{
GPIOB_BASE->ODR = 0x8953; // команда 0x2a - Column Address Set
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x1 & 0xff00)>>5); //
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x1 & 0xff) << 3); //
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x2 & 0xff00)>>5); //
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x2 & 0xff) << 3); //
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = 0x895b; // команда 0x2b - Page Address Set
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y1 & 0xff00)>>5); //
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y1 & 0xff) << 3); //
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y2 & 0xff00)>>5); //
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y2 & 0xff) << 3); //
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = 0x8963; // команда 0x2c - Mempry Write
GPIOB_BASE->BSRR = LCD_WR;
}
void Lcd_Init(void) // ILI9481 вызываем один раз, поэтому по скорости не оптимизируем
{
GPIOB_BASE->BSRR = LCD_RESET;
delay(5);
GPIOB_BASE->BRR = LCD_RESET;
delay(15);
GPIOB_BASE->BSRR = LCD_RESET;
delay(15);
Lcd_Write_Com(0x11);
delay(20);
Lcd_Write_Com(0xD0);
Lcd_Write_Data(0x07);
Lcd_Write_Data(0x42);
Lcd_Write_Data(0x18);
Lcd_Write_Com(0xD1);
Lcd_Write_Data(0x00);
Lcd_Write_Data(0x07);
Lcd_Write_Data(0x10);
Lcd_Write_Com(0xD2);
Lcd_Write_Data(0x01);
Lcd_Write_Data(0x02);
Lcd_Write_Com(0xC0);
Lcd_Write_Data(0x10);
Lcd_Write_Data(0x3B);
Lcd_Write_Data(0x00);
Lcd_Write_Data(0x02);
Lcd_Write_Data(0x11);
Lcd_Write_Com(0xC5);
Lcd_Write_Data(0x03);
Lcd_Write_Com(0x36);
Lcd_Write_Data(0x0A);
Lcd_Write_Com(0x3A);
Lcd_Write_Data(0x55);
delay(120);
Lcd_Write_Com(0x29); //Display ON
Lcd_Write_Com(0x2c); // Memory Write
}
void H_line(uint32_t x, uint32_t y, uint32_t l, uint32_t c)
{
Address_set(x, y, x+l-1, y);
for(uint32_t i=0; i<l; i++)
{
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((c >> 5) & DATA_MASK);
GPIOB_BASE->BSRR = LCD_WR;
GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((c << 3) & DATA_MASK);
GPIOB_BASE->BSRR = LCD_WR;
}
}
#define size 1024
float x[size], y[size]; // действительные и мнимые части.
float w[size]; // окно
inline void FFT() // 1024 отсчета
{
int32_t i,i1,j,k,l,l1,l2;
float c1,c2,tx,ty,t1,t2,u1,u2,z;
/* Do the bit reversal */
j = 0;
for (i = 0; i < 1023; i++) {
if (i < j) {
tx = x[i];
ty = y[i];
x[i] = x[j];
y[i] = y[j];
x[j] = tx;
y[j] = ty;
}
k = 512;
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
/* Compute the FFT */
c1 = -1.0;
c2 = 0.0;
l2 = 1;
for (l = 0; l < 10; l++) {
l1 = l2;
l2 <<= 1;
u1 = 1.0;
u2 = 0.0;
for (j = 0; j < l1; j++) {
for (i = j; i < 1024; i += l2) {
i1 = i + l1;
t1 = u1 * x[i1] - u2 * y[i1];
t2 = u1 * y[i1] + u2 * x[i1];
x[i1] = x[i] - t1;
y[i1] = y[i] - t2;
x[i] += t1;
y[i] += t2;
}
z = u1 * c1 - u2 * c2;
u2 = u1 * c2 + u2 * c1;
u1 = z;
}
c2 = -sqrt((1.0 - c1) / 2.0);
c1 = sqrt((1.0 + c1) / 2.0);
}
/* Calculate the absolute value */
for (i = 0; i < 1024; i++) {
y[i] = (y[i]*y[i] + x[i]*x[i])/1024;
}
}
inline int log216(uint32_t a) { // логарифмирование в масштабе 16 единиц на 3 дБ (5.3дБ-1)
uint32_t i = 0;
uint32_t mask1 = 0x80000000;
while((!(a & mask1)) && (i < 27)){ i++; mask1 >>= 1; }
return (i*16 + 15 - ((a >> (27-i)) & 0x0000000f));
}
void setup()
{
GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; // все управляющие HIGH
GPIOB_BASE->CRL = 0x33333444; // пины PB7-PB3 - выход, PB2-PB0 - вход (не исп.)
GPIOB_BASE->CRH = 0x33333333; // пины PB15-PB8 - выход
GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; //
Lcd_Init();
for(int i = 0; i < size; i++) {
w[i] = sin((2*PI*i)/size);
}
}
void loop()
{
int tm = micros() + 21;
for(int i = 0; i < size; i++) {
while(micros() < tm) { ; }
x[i] = (analogRead(AUDIO_INPUT) - 2048)*w[i];
y[i] = 0;
tm += 21;
}
FFT();
for(int i = 1; i < 481; i++){
uint32_t s = log216((uint32_t)(y[i] + y[1024-i])) - 74;
if(s < 0) s = 0;
if(s > 319) s = 319;
H_line(0, i-1, s, 0x001f);
H_line(s, i-1, 320-s, 0xf800);
}
}

Кстати, на экране отчетливо видна вторая гармоника амплитудой порядка -40дБ т.е. примерно 1%.
Часть этих искажений дает генератор, а часть - АЦП контроллера. По крайней мере, при уменьшении сигнала на десятки процентов вторая гармоника уменьшается на несколько дБ, но не исчезает совсем.
В общем, прибор оказался довольно чувствительным.
Да, вдогонку: как видно на фото, сигнал на вход подается амплитудой 1.6 В со смещением 1.65 В напрямую.
В случае работы с реальным сигналом, например, с аудиовыхода, естественно, необходимы дополнительные входные цепи.
В простейшем случае - это делитель из двух резисторов по 100 кОм между землей и питанием, середина которого соединена со входом контроллера. А сигнал подается в эту же точку через конденсатор 0.22 мкФ. На всякий случай последовательно с конденсатором можно включить резистор на несколько кОм.
Ну а по хорошему - на входе ФНЧ на ОУ и защита на стабилитроне.
Кстати, фильтр очень желателен: из-за высокого динамического диапазона прекрасно видны все зеркальные частоты от сигнала, спектр которого не ограничен половиной частоты дискретизации. Например, банального меандра.
#include "dma.h" #define LCD_RD 0x8000 // PB15 - не используется (если использовать, нужно согласовать уровни сигналов) #define LCD_WR 0x4000 // PB14 #define LCD_RS 0x2000 // PB13 #define LCD_CS 0x1000 // PB12 #define LCD_RESET 0x0800 // PB11 #define DATA_MASK 0x07f8 // PB3-PB10 #define CTRL_MASK_DEFAULT 0xf800 // биты PB0-PB2 не используется, поэтому не 0xf107: все управляющие биты HIGH #define CTRL_MASK_WR_RS_SC 0x8803 // маска для записи команд #define CTRL_MASK_WR_SC 0xa803 // маска для записи данных #define AUDIO_INPUT PA0 // аналоговый вход void Lcd_Write_Com(unsigned char d) // вывод команды: опустить PB13, вывести байт, поднять PB13 { GPIOB_BASE->ODR = CTRL_MASK_WR_RS_SC | (d << 3); GPIOB_BASE->BSRR = LCD_WR; } inline void Lcd_Write_Data(unsigned char d) { GPIOB_BASE->ODR = CTRL_MASK_WR_SC | (d << 3); GPIOB_BASE->BSRR = LCD_WR; } inline void Address_set(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2) // позиционирование на экране (оптимизировано) { GPIOB_BASE->ODR = 0x8953; // команда 0x2a - Column Address Set GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x1 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x2 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x895b; // команда 0x2b - Page Address Set GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y1 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y2 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x8963; // команда 0x2c - Mempry Write GPIOB_BASE->BSRR = LCD_WR; } void Lcd_Init(void) // ILI9481 вызываем один раз, поэтому по скорости не оптимизируем { GPIOB_BASE->BSRR = LCD_RESET; delay(5); GPIOB_BASE->BRR = LCD_RESET; delay(15); GPIOB_BASE->BSRR = LCD_RESET; delay(15); Lcd_Write_Com(0x11); delay(20); Lcd_Write_Com(0xD0); Lcd_Write_Data(0x07); Lcd_Write_Data(0x42); Lcd_Write_Data(0x18); Lcd_Write_Com(0xD1); Lcd_Write_Data(0x00); Lcd_Write_Data(0x07); Lcd_Write_Data(0x10); Lcd_Write_Com(0xD2); Lcd_Write_Data(0x01); Lcd_Write_Data(0x02); Lcd_Write_Com(0xC0); Lcd_Write_Data(0x10); Lcd_Write_Data(0x3B); Lcd_Write_Data(0x00); Lcd_Write_Data(0x02); Lcd_Write_Data(0x11); Lcd_Write_Com(0xC5); Lcd_Write_Data(0x03); Lcd_Write_Com(0x36); Lcd_Write_Data(0x0A); Lcd_Write_Com(0x3A); Lcd_Write_Data(0x55); delay(120); Lcd_Write_Com(0x29); //Display ON Lcd_Write_Com(0x2c); // Memory Write } void H_line(uint32_t x, uint32_t y, uint32_t l, uint32_t c) { Address_set(x, y, x+l-1, y); for(uint32_t i=0; i<l; i++) { GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((c >> 5) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((c << 3) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; } } #define size 1024 float x[size], y[size]; // действительные и мнимые части. float w[size]; // окно int16_t ADCBuffer[size], ADCBuffer2[size]; int16_t * buffADC, * buffFFT; inline void FFT() // 1024 отсчета { int32_t i,i1,j,k,l,l1,l2; float c1,c2,tx,ty,t1,t2,u1,u2,z; /* Do the bit reversal */ j = 0; for (i = 0; i < 1023; i++) { if (i < j) { tx = x[i]; ty = y[i]; x[i] = x[j]; y[i] = y[j]; x[j] = tx; y[j] = ty; } k = 512; while (k <= j) { j -= k; k >>= 1; } j += k; } /* Compute the FFT */ c1 = -1.0; c2 = 0.0; l2 = 1; for (l = 0; l < 10; l++) { l1 = l2; l2 <<= 1; u1 = 1.0; u2 = 0.0; for (j = 0; j < l1; j++) { for (i = j; i < 1024; i += l2) { i1 = i + l1; t1 = u1 * x[i1] - u2 * y[i1]; t2 = u1 * y[i1] + u2 * x[i1]; x[i1] = x[i] - t1; y[i1] = y[i] - t2; x[i] += t1; y[i] += t2; } z = u1 * c1 - u2 * c2; u2 = u1 * c2 + u2 * c1; u1 = z; } c2 = -sqrt((1.0 - c1) / 2.0); c1 = sqrt((1.0 + c1) / 2.0); } /* Calculate the absolute value */ for (i = 0; i < 1024; i++) { y[i] = (y[i]*y[i] + x[i]*x[i])/1024; } } inline int log216(uint32_t a) { // логарифмирование в масштабе 16 единиц на 3 дБ (5.3дБ-1) uint32_t i = 0; uint32_t mask1 = 0x80000000; while((!(a & mask1)) && (i < 27)){ i++; mask1 >>= 1; } return (i*16 + 15 - ((a >> (27-i)) & 0x0000000f)); } void ADC_DMA_init(void) { int a = analogRead(PA0); Serial.print("analogRead: "); Serial.println(a, HEX); Serial.print("ADC1_BASE->DR: "); Serial.println(ADC1_BASE->DR, HEX); // GPIOA_BASE->CRL = 0x444444b0; // пины PA7-PA2 - вход, PA0 - аналоговый вход, PA1 - выход таймера (для отладки) RCC_BASE->AHBENR |= 1; //DMA1EN - вкл.тактирования DMA1, все остальное (таймер, порты, ADC, RCC_PCLK2_Div6) stm32duino по умолчанию уже включило // DMA1->CCR1: MEM2MEM=0, PL=2, MSIZE=01, PSIZE=01, MINC=1, PINC=0, CIRC=0, DIR=0, TEIE=0, HTIE=0, TCIE=0, EN=1 DMA1_BASE->CCR1 |= 0x2000; // PL=2 (bits 13-12) - high DMA1_BASE->CCR1 |= 0x0400; // MSIZE=01 (bits 11-10) - 16 bits DMA1_BASE->CCR1 |= 0x0100; // PIZE=01 (bits 9-8) - 16 bits DMA1_BASE->CCR1 |= 0x0080; // MINC (bit 7) инкрементировать адреса в памяти DMA1_BASE->CNDTR1 = size; // сколько сэмплов передать (read only when channel disabled) DMA1_BASE->CPAR1 = (uint32_t)&ADC1_BASE->DR; // читать из ADC1 DMA1_BASE->CMAR1 = (uint32_t)buffADC; // куда писать DMA1_BASE->CCR1 |= 1; // EN (bit 0) 0 включаем Serial.print("DMA1_BASE->ISR: "); Serial.println(DMA1_BASE->ISR, HEX); Serial.print("DMA1_BASE->IFCR: "); Serial.println(DMA1_BASE->IFCR, HEX); Serial.print("DMA1_BASE->CCR1: "); Serial.println(DMA1_BASE->CCR1, HEX); Serial.print("DMA1_BASE->CNDTR1: "); Serial.println(DMA1_BASE->CNDTR1, HEX); Serial.print("DMA1_BASE->CPAR1: "); Serial.println(DMA1_BASE->CPAR1, HEX); Serial.print("DMA1_BASE->CMAR1: "); Serial.println(DMA1_BASE->CMAR1, HEX); Serial.print("ADC1_BASE->DR: "); Serial.println(ADC1_BASE->DR, HEX); // ADC1->CR1: AWDEN=0, JAWDEN=0, DUALMOD=0(Independed), DISCNUM=0, JDISCEN=0, DISCEN=0, JAUTO=0, AWDSGL=0, SCAN=0, JEOCIE=0, AWDIE=0, EOCIE=0, AWDCH=0 // ADC1->CR2: TSVREFE=0, SWSTART=0, JSWSTART=0, EXTTRIG=1, EXTCEL=3(Tim2-CC2), JEXTTRIG=0, JEXTCEL=0, ALIGN=0, DMA=1, RSTCAL=0, CAL=0, CONT=0, ADON=1 // ADC1->SQR1.l=0 (one channel), SQR3.SQ1=0 (first channel) ADC1_BASE->CR2 |= 0x00100000; // EXTTRIG (bit 20) //////// ADC1_BASE->CR2 |= 0x00000002; // CONT=1 (bit 1) - это непрерывное преобразование без таймера ??? ADC1_BASE->CR2 &= 0xfff1ffff; ADC1_BASE->CR2 |= 0x00060000; // EXTCEL=3 (bit 19-17) Tim2CC2 ADC1_BASE->CR2 |= 0x00000100; // DMA (bit 8) ADC1_BASE->CR2 |= 1; // ADON Serial.print("ADC1_BASE->SR: "); Serial.println(ADC1_BASE->SR, HEX); Serial.print("ADC1_BASE->CR1: "); Serial.println(ADC1_BASE->CR1, HEX); Serial.print("ADC1_BASE->CR2: "); Serial.println(ADC1_BASE->CR2, HEX); Serial.print("ADC1_BASE->SQR1: "); Serial.println(ADC1_BASE->SQR1, HEX); Serial.print("ADC1_BASE->SQR2: "); Serial.println(ADC1_BASE->SQR2, HEX); Serial.print("ADC1_BASE->SQR3: "); Serial.println(ADC1_BASE->SQR3, HEX); Serial.print("ADC1_BASE->DR: "); Serial.println(ADC1_BASE->DR, HEX); // default by stm32duino: TIMER2->CR1.ARPE=1, CR1.CEN=1, CCMR1/2=[0x68], DMAR=0x81 (?) // TIMER2->ARR=749, CCR2=374, EGR.CC2G=1, EGR.UG=1 (?), CCER.CC2E=1 TIMER2_BASE->ARR = 749; // 36000k/750=48k TIMER2_BASE->CCR2 = 374; // 750/2=375 TIMER2_BASE->EGR = 0x0005; // не уверен, что это нужно TIMER2_BASE->CCER = 0x0010; // CC2 enable } void inline resetDMA(){ DMA1_BASE->CCR1 &= 0xfffffffe; // EN (bit 0) DMA1_BASE->CNDTR1 = 1024; DMA1_BASE->IFCR = 0x0000000f; int16_t * tmp = buffFFT; buffFFT = buffADC; buffADC = tmp; DMA1_BASE->CMAR1 = (uint32_t)buffADC; // куда писать DMA1_BASE->CCR1 |= 1; } void setup() { Serial.begin(9600); while (!Serial) { ; } // wait for serial port to connect. GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; // все управляющие HIGH GPIOB_BASE->CRL = 0x33333444; // пины PB7-PB3 - выход, PB2-PB0 - вход (не исп.) GPIOB_BASE->CRH = 0x33333333; // пины PB15-PB8 - выход GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; // GPIOA_BASE->CRL = 0x444444b0; // пины PA7-PA2 - вход (не исп.), PA0 - аналоговый вход, PA1 - выход таймера (для отладки) Lcd_Init(); buffADC = ADCBuffer; buffFFT = ADCBuffer2; ADC_DMA_init(); for(int i = 0; i < size; i++) { buffADC[i] = 0; buffFFT[i] = 0; // x[i] = 0; // y[i] = 0; } for(int i = 0; i < size; i++) { w[i] = sin((2*PI*i)/size); } } void loop() { int t0 = millis(); /* int tm = micros() + 21; for(int i = 0; i < size; i++) { while(micros() < tm) { ; } x[i] = (analogRead(AUDIO_INPUT) - 2048)*w[i]; y[i] = 0; tm += 21; } */ while((DMA1_BASE->ISR & 2)== 0) { ; } resetDMA(); // while((DMA1_BASE->ISR & 2)== 0) { ; } for(int i = 0; i < size; i++) { x[i] = (buffFFT[i] - 2048)*w[i]; y[i] = 0; } FFT(); for(int i = 1; i < 481; i++){ uint32_t s = log216((uint32_t)(y[i] + y[1024-i])) - 74; if(s < 0) s = 0; if(s > 319) s = 319; H_line(0, i-1, s, 0x001f); H_line(s, i-1, 320-s, 0xf800); } int t1 = millis(); Serial.println(t1-t0); }А с чем связна такая форма обращения к регистрам:
ppp_BASE->rrr? Обычно все ж пишут DMA1_Channel1->CCR, а не DMA1_BASE->CCR.Ну эту форму не я придумал. Почему-то и GPIO и TIMERn - все задаются через *_BASE. И, кстати, в комплекте Arduino IDE есть файлик dma.h, в котором DMA подключается именно таким образом.
Я понял -- это старый вариант STM32DUINO. Возможно, имеет смысл задуматься о переходе на новый.
Я нашёл на форуме упоминание Роджера Кларка (автора основной библиотеки и основателя/модератора Stm32duino.com) про существование других вариантов аддонов под ардуино: https://www.stm32duino.com/viewtopic.php?t=97 В общем он пишет, что этот вариант от самих STM, правда на st.com об этом репозитории никаких упоминаний не нашёл, т.к. что возможно что это пока делается полуофициально. На форуме stm32duino есть отдельный раздел посвящённый этому ядру http://stm32duino.com/viewforum.php?f=48
Вопрос переходить или нет на новое ядро от stm пока остаётся открытым... :)
Честно говоря, я не любитель менять что-то на более новое, пока это "что-то" работает.
Это - первое.
Второе:
Когда искал примеры/аналоги работы с DMA, обнаружил, что гораздо больше исходников, ориентированных на keil (что, впрочем, логично). Но это немного более высокий уровень. Думаю, что с keil рано или поздно познакомиться придется, но для начала лучше разобраться на низком уровне - через регистры - что как раз и дает Ардуино. А, судя по записям в "новом" варианте, там уже обращение идет не к регистрам, а к битовым полям внутри регистра.
Нет никакой отдельной сущности регистров в виде битовых полей. Разряды регистра и биты этих самыых "полей" -- суть одно и то же. То, что у Кларка может быть записано так:
на CMSIS будет выглядеть так:
Никаких волшебных цифр, самодокументируемый, по сути, код. Без проблем можно таскать куски кода между ардуиной и другими средами (кейл, иар и пр.).
Нет смысла набивать руку на кларковских изобретениях, т.к. это дополнительно ничего не дает, но кроме конкретно этой реализации более неприменимо нигде.
Всё таки из принципа покопался, и нашёл на st.com ссылку на ардуиновский аддон, она была в мануале к одной из отладочных плат. Поставил аддон, из того что бросилось в глаза -нет загрузки с бутлоадера. :(
А разве "Serial" -- это не оно?
a5021, ну это аппаратный сериал с выходом пинов на плату, то есть требует отдельного переходника. А в родном аддоне был ещё полусофтовый сериал (USB-CDC), который позволял через usb-разъём прошиваться.
Я с такими тонкостями дел не имел, т.к. привык для плат без лоадера просто внешний программатор цеплять. С другой стороны, наверняка есть возможность использовать этот USB-CDC и с Arduino_Core_STM32. Те же NUCLEO-64 имеют возможность заливки скетчей вообще через MassStorage, а одна из этих плат, как раз на F103.
Ну, раз уж разговор ушел в сторону: никто не имел дела с stm32f407?
Вчера пришла плата. В документации, вроде, написано, что загрузчик для нее в отличие от f103 не нужен, т.к. есть изначально. Но что-то моя Винда говорит - неопознанное устройство. Я так понимаю, нужен драйвер, но драйвер от f103 не подходит, а поиск по и-нету пока результатов не дал.
Что хоть за плата?
Плата такая: https://ru.aliexpress.com/item/1-STM32F407VET6-Cortex-M4-STM32/32864003267.html
Может оказаться, что только дядюшке Ляо известно о прошивке внутри и том, какие драйвера под это дело потребны.
andriano, мапловский может не подходить из-за того, что виды-пиды там нестандартные вписаны. Может попробовать родной драйвер?
Драйвер здесь
Плата такая: https://ru.aliexpress.com/item/1-STM32F407VET6-Cortex-M4-STM32/32864003267.html
ну и моща... почти 200 МГц, полмегабайта памяти, почти сотня GPIO, сеть и прочие интерфейсы на борту.
Куда только все это использовать в таком формате...
Andriano - если есть задумки, подо что брали плату - поделитесь, интересно.
1. Увы, ни одна из ссылок не помогла.
2. Просто заинтересовался stm32, захотелось попробовать что-нибудь посерьезнее f103. Тут, вроде, и интерфейс для камеры и FPU, так что для чего-нибудь, думаю, пригодится.
И китаец не отвечает, похоже, все ушли в запой - у них сейчас главный государственный праздник. Если до понедельника не прочухается, буду открывать спор.
Может, ее не через USB, а через JTAC/SWD надо подключать?
Может, ее не через USB, а через JTAC/SWD надо подключать?
а может не тот USB - в описании говорится, что на плате аж три распаянных разъема. Да еще и посмотрите, как оно распаяно - помните - на F103 надо было резистор напаять, чтобы USB опознался.
Наверное пора переходить с 4-е измерение, потому как в этих я двух других USB разъемомв не вижу
У меня на двух компах вполне распознается без дополнительных резисторов все 4 имеющиеся платы при том, что они явно относятся к двум слегка отличающимся семействам. Не распознается лишь одна - как раз та, на которую резисто был напаян (в попытках ее запустить, но реанимировать ее не удалось). Кстати, на этой плаие, если верить схеме, на USB стоит как раз 1.5 кОм.
Может, ее не через USB, а через JTAC/SWD надо подключать?
А вы прям где-то прочли, что с заводской прошивкой можно через USB-CDC можно прошиваться?
Где-то прочел: https://wiki.stm32duino.com/index.php?title=Burning_the_bootloader
Most STM32F103 boards do not come with a USB bootloader installed. The main exception is the Maple mini. The STM32F4 series MCU has a built in Serial and USB (DFU) bootloader, so there is no need to install an additional bootloader.
Может я это как-то неправильно понял?
Boot select правильно выставил?
Вот сейчас спросил у Гугла: "boot select - что это такое". Но, увы, среди предложенных им вариантов ничего толкового не обнаружил.
Чтобы было понятно: с "голым" кристаллом никогда в жизни не работал и не собираюсь, во фьюзах AVR никогда не пытался разбираться и даже не знаю, существует ли что-нибудь аналогичное у stm32. SMD никогда не паял и не собираюсь, т.к. DIP 2.54 паяю в двух разных очках, причем эти очки не совпадают с теми, в которых читаю. А потому раз и навсегда решил для себя, что имею дело только с готовыми платами либо DIP микросхемами и не-SMD рассыпухой.
Да, если boot select - это те два джампера на плате, которыми каким-то образом (не разбирался, каким) определяется порядок загрузки, то они у меня всегда на всех платах стоят в положении по умолчанию - 00. Насколько мне известно, и Arduino и ST-Link работают с платой именно в этом режиме.
PS. Сейчас буду пробовать подключить через ST-Link.
Загрузчики находятся в System memory
Насколько я понял, этот тот режим, в котором нужно прошивать через Serial.
Но я попытался сделать по другому: сначала, естественно, на "кошечках", т.е. прошил блинк в f103 через ST-Link. А когда убедился, что это работает, выяснил, что светодиоды на f407 подключены на PA6 и PA7, подправил блинк под f407 и прошил его. Судя по поведению светодиодов - успешно.
Теперь вопрос - а отлаживаться-то как? Без текстовой консоли - неудобно.
PS. Еще непонятка - почему-то Arduino IDE сообщает только о 128К оперативки, хотя, вроде, должно быть 196К.
В принципе давно собирался попробовать Keil, но все оказии не подворачивалось. Ну и немного смущает использование инструментального средства с ограничеием 32к для камня, у которого 512к. И где-то здесь читал, что Keil требует обновления прошивки ST-Link.
Ладно, возвращаюсь к топику.
Как-то на рабочей неделе не получаетя выкроить время для проекта, поэтому действительно получается "проект выходного дня", но не на одну неделю.
Сейчас нахожусь в размышлении, как лучше сделать управление режимами работы. Хочется иметь возможность переключать диапазон (т.е. по сути - частоту дискретизации) и помещать (имли убирать) поверх спектра еще осциллограмму и частоту, если будет обнаружено, что сигнал периодический.
Хотел вообще отказаться от отдельных органов управления, но оказалось, что имеющийся у меня экран 320х480 не сенсорный. И, похоже, другие экраны с таким же разрешением - такие же.
Значит - энкодер. Проблема лишь в том, достаточно ли его будет или потребуются дополнительные кнопки.
Ну и подключение самого энкодера: stm32 позволяет подключить энкодер без использования ЦПУ, - используя возможноси аппаратного таймера. Но такое решение ограничивает в выборе пинов. Пока не определился, что лучше: такое аппаратное решение или стандартное решение на прерываниях.
Может я это как-то неправильно понял?
1. Переключил джампер на загрузку из системной памяти, в диспетчере устройств появился stm32 BOOTLOADER, и как им теперь воспользоваться?
На попытку прошивки через Arduino IDE отвечает:
Честно говоря, по этой диагностике я понял, что с ком-портом - дело труба, а вот с DFU - то-ли нашел (в строках 13-16), то-ли не может найти (строка 10) - какая-то противоречивая информация.
По факту - светодиоды должным образом не заработали (точнее, вообще не заработали).
Что делать дальше, как им пользоваться?
Что делать дальше, как им пользоваться?
Без ручных манипуляций не обойтись, в этом смысле такой вид загрузки не очень удобен.
Тогда непонятна фраза "В F103 нет USB загрузчика. В F4 есть и USB и Serial загрузчики.".
stm32f103 при перестановке перемычек в положение "10" можно прошить через последовательный порт: https://habr.com/post/395577/
stm32f407 - точно так же при перестановке перемычек в положение "10" можно прошить через последовательный порт.
При этом ни один из них в режиме "00" прошить через встроенный порт нельзя.
Так в чем же разница? Почему считается, что у одного есть загрузчик, а у другого - нет?
Далее: мы прошиваем специальный загрузчик, чтобы была возможность пользоваться Arduino IDE через USB порт. В цитате с stm32duino, которую я приводил в сообщении №24, утверждается, что для stm32f407 прошивка загрузчика не нужна, т.к. он уже есть.
В общем, у меня одно с другим никак в сознании не складывается. Так в чем разница между 103 и 407 относительно штатного загрузчика с точки зрения использования Arduino IDE?
Так в чем же разница? Почему считается, что у одного есть загрузчик, а у другого - нет?
Проект постепенно продвигается: нарисовал и распечатал корпус и конструктивные элементы, соделал экран "полосатым" - через 3 дБ теперь неяркие горизонтальные полоски. Заодно за счет разворачивания циклов немного подросла скорость - тепеь примерно 11 fps.
При помощи энкодера сделал переключение между 5-ю диапазонами измерения: 5 кГц, 10 кГц, 20 кГц, 40 кГц и 60 кГц. Частотный диапазон ограничен сверху фильтром на микросхеме MAX261, но об этом позже.
Краем уха услышал, что таймеры в stm32 можно использовать для аппаратной поддержки энкодера. В принципе, на прерываниях привычнее, но надо же осваивать новые технологии! Тем более, что свободные таймеры есть: один используется для задания частоты дискретизации звука, другой - для управления фильтром, третий - для энкодера и еще один остается в запасе.
Правда, энкодер работает как-то странно: то на один щелчок изменяет счетчик на 1, то на 2. У меня даже сложилось впечатление, что сначала некоторое время - на 1, а потом переключаеся в режим - на 2. Чтобы не было пропусков, делю значение счетчика на 2 - тогда сначала происходит переключение не на каждый щелчок. Но все лучше, чем если бы перескакивал.
Выяснилось, что высокочастотный сигнал управления фильтром (до 4 МГц) проникает в звуковой тракт и прекрасно виден на экране спектранализатора. Естественно, не сами 4 МГц, а разностные частоты. А разностная частота в данном случае была 4000 - 48*83 = 16 кГц. Вот она и была видна, пока частота дискретизации составляла 48 кГц. Пришлось подкорректировать частоту дискретизации так, чтобы она делила 4 МГц нацело.
Код:
#include "dma.h" #define LCD_RD // не исп. // 0x8000 // PB15 - не используется (если использовать, нужно согласовать уровни сигналов) #define LCD_WR 0x1000 // PB12 // 0x4000 // PB14 #define LCD_RS 0x0800 // PB11 // 0x2000 // PB13 #define LCD_CS 0x0002 // PB1 // 0x1000 // PB12 #define LCD_RESET 0x0001 // PB0 // 0x0800 // PB11 #define DATA_MASK 0x07f8 // PB3-PB10, данные передаются в разрядах: D0 - PB3, D1 - PB4, ... D7 - PB10 #define CTRL_MASK_DEFAULT 0x1803 // 0xf800 // все управляющие биты HIGH #define CTRL_MASK_WR_RS_CS 0x0001 // 0x8803 // маска для записи команд #define CTRL_MASK_WR_CS 0x0801 // 0xa803 // маска для записи данных #define AUDIO_INPUT PA0 // аналоговый вход #define size 1024 // длина буфера оцифровки float x[size], y[size]; // действительные и мнимые части. int16_t ADCBuffer[size], ADCBuffer2[size]; // буфера для поочередного ввода данных с АЦП через ПДП (второй буфер обрабатывается ЦПУ) int16_t * buffADC, * buffFFT; // буфера для ADC/DMA и CPU соответственно byte stateA = 2; // частота оцифровки ADC: 0 - 11.9кГц, 1 - 23.8кГц, 3 - 47.6кГц, 4 - 95.2кГц, 5 - 142.9кГц byte stateF = 2; // частота упр.фильтром(фильтра): 0 - 0.5МГц(5.00), 1 - 1МГц(10.00), 2 - 2МГц(20.00), 3 - 4МГц(40.00), 4 - 4МГц(56.27) const uint16_t dividorADC[5] = {3023, 1511, 755, 377, 251}; // делитель частоты 36 МГц до частоты дискретизации const uint16_t dividorFilter[5] = {71, 35, 17, 8, 8}; // делитель частоты 36 МГц до управляющей частоты фильтра const uint8_t filterFreqCode[5] = {26, 26, 26, 26, 0}; // управляющий код фильтра (соответствует отношениям {100,100,100,100,71}) const uint8_t stepScale[5] = {1, 1, 2, 5, 10}; const uint8_t maxScale[5] = {5, 10, 20, 40, 60}; const uint32_t screenScale[5] = {87936, 43968, 21984, 10992, 7328}; // 10922 void Lcd_Write_Com(unsigned char d) { // вывод команды: опустить PB13, вывести байт, поднять PB13 GPIOB_BASE->ODR = CTRL_MASK_WR_RS_CS | (d << 3); GPIOB_BASE->BSRR = LCD_WR; } inline void Lcd_Write_Data(unsigned char d) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS | (d << 3); GPIOB_BASE->BSRR = LCD_WR; } inline void Lcd_Write_Pixel(uint16_t d) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((d & 0xff00) >> 5); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((d & 0xff) << 3); GPIOB_BASE->BSRR = LCD_WR; } inline void Address_set(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2) { // позиционирование на экране (оптимизировано) GPIOB_BASE->ODR = 0x0151; //0x8953; // команда 0x2a - Column Address Set // -8803 + 0001 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((x1 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((x1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((x2 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((x2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x0159;//0x895b; // команда 0x2b - Page Address Set GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((y1 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((y1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((y2 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((y2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x161; //0x8963; // команда 0x2c - Mempry Write GPIOB_BASE->BSRR = LCD_WR; } void Lcd_Init(void) { // ILI9481 вызываем один раз, поэтому по скорости не оптимизируем GPIOB_BASE->BSRR = LCD_RESET; delay(5); GPIOB_BASE->BRR = LCD_RESET; delay(15); GPIOB_BASE->BSRR = LCD_RESET; delay(15); Lcd_Write_Com(0x11); delay(20); Lcd_Write_Com(0xD0); Lcd_Write_Data(0x07); Lcd_Write_Data(0x42); Lcd_Write_Data(0x18); Lcd_Write_Com(0xD1); Lcd_Write_Data(0x00); Lcd_Write_Data(0x07); Lcd_Write_Data(0x10); Lcd_Write_Com(0xD2); Lcd_Write_Data(0x01); Lcd_Write_Data(0x02); Lcd_Write_Com(0xC0); Lcd_Write_Data(0x10); Lcd_Write_Data(0x3B); Lcd_Write_Data(0x00); Lcd_Write_Data(0x02); Lcd_Write_Data(0x11); Lcd_Write_Com(0xC5); Lcd_Write_Data(0x03); Lcd_Write_Com(0x36); Lcd_Write_Data(0x0A); Lcd_Write_Com(0x3A); Lcd_Write_Data(0x55); delay(120); Lcd_Write_Com(0x29); //Display ON Lcd_Write_Com(0x2c); // Memory Write } void H_line(uint32_t x, uint32_t y, uint32_t l, uint32_t c) { Address_set(x, y, x+l-1, y); for(uint32_t i=0; i<l; i++) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((c >> 5) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((c << 3) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; } } void V_line(uint32_t x, uint32_t y, uint32_t w, uint32_t c) { Address_set(x, y, x, y+w-1); for(uint32_t i=0; i<w; i++) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((c >> 5) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((c << 3) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; } } const float w[size] = { 0.00153398, 0.00460193, 0.00766983, 0.01073766, 0.01380539, 0.01687299, 0.01994043, 0.02300768, 0.02607472, 0.02914151, 0.03220803, 0.03527424, 0.03834012, 0.04140564, 0.04447077, 0.04753548, 0.05059975, 0.05366354, 0.05672682, 0.05978957, 0.06285176, 0.06591335, 0.06897433, 0.07203465, 0.07509430, 0.07815324, 0.08121145, 0.08426889, 0.08732554, 0.09038136, 0.09343634, 0.09649043, 0.09954362, 0.10259587, 0.10564715, 0.10869744, 0.11174671, 0.11479493, 0.11784206, 0.12088809, 0.12393298, 0.12697670, 0.13001922, 0.13306053, 0.13610058, 0.13913934, 0.14217680, 0.14521292, 0.14824768, 0.15128104, 0.15431297, 0.15734346, 0.16037246, 0.16339995, 0.16642590, 0.16945029, 0.17247308, 0.17549425, 0.17851377, 0.18153161, 0.18454774, 0.18756213, 0.19057475, 0.19358559, 0.19659460, 0.19960176, 0.20260704, 0.20561041, 0.20861185, 0.21161133, 0.21460881, 0.21760427, 0.22059769, 0.22358903, 0.22657826, 0.22956537, 0.23255031, 0.23553306, 0.23851359, 0.24149189, 0.24446790, 0.24744162, 0.25041301, 0.25338204, 0.25634868, 0.25931292, 0.26227471, 0.26523403, 0.26819086, 0.27114516, 0.27409691, 0.27704608, 0.27999264, 0.28293657, 0.28587783, 0.28881641, 0.29175226, 0.29468537, 0.29761571, 0.30054324, 0.30346795, 0.30638980, 0.30930876, 0.31222481, 0.31513793, 0.31804808, 0.32095523, 0.32385937, 0.32676045, 0.32965846, 0.33255337, 0.33544515, 0.33833377, 0.34121920, 0.34410143, 0.34698041, 0.34985613, 0.35272856, 0.35559766, 0.35846342, 0.36132581, 0.36418479, 0.36704035, 0.36989245, 0.37274107, 0.37558618, 0.37842775, 0.38126577, 0.38410020, 0.38693101, 0.38975817, 0.39258167, 0.39540148, 0.39821756, 0.40102990, 0.40383846, 0.40664322, 0.40944415, 0.41224123, 0.41503442, 0.41782372, 0.42060907, 0.42339047, 0.42616789, 0.42894129, 0.43171066, 0.43447596, 0.43723717, 0.43999427, 0.44274723, 0.44549602, 0.44824061, 0.45098099, 0.45371712, 0.45644898, 0.45917655, 0.46189979, 0.46461869, 0.46733321, 0.47004333, 0.47274903, 0.47545028, 0.47814706, 0.48083933, 0.48352708, 0.48621028, 0.48888890, 0.49156292, 0.49423231, 0.49689705, 0.49955711, 0.50221247, 0.50486311, 0.50750899, 0.51015010, 0.51278640, 0.51541788, 0.51804450, 0.52066625, 0.52328310, 0.52589503, 0.52850200, 0.53110400, 0.53370100, 0.53629298, 0.53887991, 0.54146177, 0.54403853, 0.54661017, 0.54917666, 0.55173799, 0.55429412, 0.55684504, 0.55939071, 0.56193112, 0.56446624, 0.56699605, 0.56952052, 0.57203963, 0.57455336, 0.57706167, 0.57956456, 0.58206199, 0.58455394, 0.58704039, 0.58952132, 0.59199669, 0.59446650, 0.59693071, 0.59938930, 0.60184225, 0.60428953, 0.60673113, 0.60916701, 0.61159716, 0.61402156, 0.61644017, 0.61885299, 0.62125998, 0.62366112, 0.62605639, 0.62844577, 0.63082923, 0.63320676, 0.63557832, 0.63794390, 0.64030348, 0.64265703, 0.64500454, 0.64734597, 0.64968131, 0.65201053, 0.65433362, 0.65665055, 0.65896129, 0.66126584, 0.66356416, 0.66585623, 0.66814204, 0.67042156, 0.67269477, 0.67496165, 0.67722217, 0.67947632, 0.68172407, 0.68396541, 0.68620031, 0.68842875, 0.69065071, 0.69286617, 0.69507511, 0.69727751, 0.69947334, 0.70166259, 0.70384524, 0.70602126, 0.70819064, 0.71035335, 0.71250937, 0.71465869, 0.71680128, 0.71893712, 0.72106620, 0.72318849, 0.72530397, 0.72741263, 0.72951444, 0.73160938, 0.73369744, 0.73577859, 0.73785281, 0.73992010, 0.74198041, 0.74403374, 0.74608007, 0.74811938, 0.75015165, 0.75217685, 0.75419498, 0.75620600, 0.75820991, 0.76020668, 0.76219630, 0.76417874, 0.76615399, 0.76812203, 0.77008284, 0.77203640, 0.77398269, 0.77592170, 0.77785340, 0.77977779, 0.78169483, 0.78360452, 0.78550683, 0.78740175, 0.78928925, 0.79116933, 0.79304196, 0.79490713, 0.79676481, 0.79861499, 0.80045766, 0.80229280, 0.80412038, 0.80594039, 0.80775282, 0.80955764, 0.81135485, 0.81314441, 0.81492633, 0.81670057, 0.81846713, 0.82022598, 0.82197712, 0.82372051, 0.82545615, 0.82718403, 0.82890411, 0.83061640, 0.83232087, 0.83401750, 0.83570628, 0.83738720, 0.83906024, 0.84072537, 0.84238260, 0.84403190, 0.84567325, 0.84730664, 0.84893206, 0.85054948, 0.85215890, 0.85376030, 0.85535366, 0.85693898, 0.85851622, 0.86008539, 0.86164646, 0.86319942, 0.86474426, 0.86628095, 0.86780950, 0.86932987, 0.87084206, 0.87234606, 0.87384184, 0.87532940, 0.87680872, 0.87827979, 0.87974259, 0.88119711, 0.88264334, 0.88408126, 0.88551086, 0.88693212, 0.88834503, 0.88974959, 0.89114576, 0.89253356, 0.89391295, 0.89528392, 0.89664647, 0.89800058, 0.89934624, 0.90068343, 0.90201214, 0.90333237, 0.90464409, 0.90594730, 0.90724198, 0.90852812, 0.90980571, 0.91107473, 0.91233518, 0.91358705, 0.91483031, 0.91606497, 0.91729100, 0.91850839, 0.91971715, 0.92091724, 0.92210867, 0.92329142, 0.92446547, 0.92563083, 0.92678747, 0.92793539, 0.92907458, 0.93020502, 0.93132671, 0.93243963, 0.93354377, 0.93463913, 0.93572569, 0.93680344, 0.93787238, 0.93893248, 0.93998375, 0.94102618, 0.94205974, 0.94308444, 0.94410026, 0.94510719, 0.94610523, 0.94709437, 0.94807459, 0.94904588, 0.95000825, 0.95096167, 0.95190614, 0.95284165, 0.95376819, 0.95468575, 0.95559433, 0.95649392, 0.95738450, 0.95826607, 0.95913862, 0.96000215, 0.96085663, 0.96170208, 0.96253847, 0.96336580, 0.96418406, 0.96499325, 0.96579336, 0.96658437, 0.96736629, 0.96813910, 0.96890280, 0.96965739, 0.97040284, 0.97113916, 0.97186634, 0.97258437, 0.97329325, 0.97399296, 0.97468351, 0.97536489, 0.97603708, 0.97670009, 0.97735390, 0.97799851, 0.97863392, 0.97926012, 0.97987710, 0.98048486, 0.98108339, 0.98167269, 0.98225274, 0.98282355, 0.98338511, 0.98393741, 0.98448046, 0.98501423, 0.98553874, 0.98605396, 0.98655991, 0.98705657, 0.98754394, 0.98802202, 0.98849079, 0.98895026, 0.98940043, 0.98984128, 0.99027281, 0.99069503, 0.99110791, 0.99151147, 0.99190570, 0.99229059, 0.99266614, 0.99303235, 0.99338921, 0.99373672, 0.99407488, 0.99440368, 0.99472312, 0.99503320, 0.99533391, 0.99562526, 0.99590723, 0.99617983, 0.99644305, 0.99669690, 0.99694136, 0.99717644, 0.99740213, 0.99761844, 0.99782535, 0.99802287, 0.99821100, 0.99838974, 0.99855907, 0.99871901, 0.99886955, 0.99901069, 0.99914242, 0.99926475, 0.99937767, 0.99948119, 0.99957530, 0.99966000, 0.99973529, 0.99980117, 0.99985764, 0.99990470, 0.99994235, 0.99997059, 0.99998941, 0.99999882, 0.99999882, 0.99998941, 0.99997059, 0.99994235, 0.99990470, 0.99985764, 0.99980117, 0.99973529, 0.99966000, 0.99957530, 0.99948119, 0.99937767, 0.99926475, 0.99914242, 0.99901069, 0.99886955, 0.99871901, 0.99855907, 0.99838974, 0.99821100, 0.99802287, 0.99782535, 0.99761844, 0.99740213, 0.99717644, 0.99694136, 0.99669690, 0.99644305, 0.99617983, 0.99590723, 0.99562526, 0.99533391, 0.99503320, 0.99472312, 0.99440368, 0.99407488, 0.99373672, 0.99338921, 0.99303235, 0.99266614, 0.99229059, 0.99190570, 0.99151147, 0.99110791, 0.99069503, 0.99027281, 0.98984128, 0.98940043, 0.98895026, 0.98849079, 0.98802202, 0.98754394, 0.98705657, 0.98655991, 0.98605396, 0.98553874, 0.98501423, 0.98448046, 0.98393741, 0.98338511, 0.98282355, 0.98225274, 0.98167269, 0.98108339, 0.98048486, 0.97987710, 0.97926012, 0.97863392, 0.97799851, 0.97735390, 0.97670009, 0.97603708, 0.97536489, 0.97468351, 0.97399296, 0.97329325, 0.97258437, 0.97186634, 0.97113916, 0.97040284, 0.96965739, 0.96890280, 0.96813910, 0.96736629, 0.96658437, 0.96579336, 0.96499325, 0.96418406, 0.96336580, 0.96253847, 0.96170208, 0.96085663, 0.96000215, 0.95913862, 0.95826607, 0.95738450, 0.95649392, 0.95559433, 0.95468575, 0.95376819, 0.95284165, 0.95190614, 0.95096167, 0.95000825, 0.94904588, 0.94807459, 0.94709437, 0.94610523, 0.94510719, 0.94410026, 0.94308444, 0.94205974, 0.94102618, 0.93998375, 0.93893248, 0.93787238, 0.93680344, 0.93572569, 0.93463913, 0.93354377, 0.93243963, 0.93132671, 0.93020502, 0.92907458, 0.92793539, 0.92678747, 0.92563083, 0.92446547, 0.92329142, 0.92210867, 0.92091724, 0.91971715, 0.91850839, 0.91729100, 0.91606497, 0.91483031, 0.91358705, 0.91233518, 0.91107473, 0.90980571, 0.90852812, 0.90724198, 0.90594730, 0.90464409, 0.90333237, 0.90201214, 0.90068343, 0.89934624, 0.89800058, 0.89664647, 0.89528392, 0.89391295, 0.89253356, 0.89114576, 0.88974959, 0.88834503, 0.88693212, 0.88551086, 0.88408126, 0.88264334, 0.88119711, 0.87974259, 0.87827979, 0.87680872, 0.87532940, 0.87384184, 0.87234606, 0.87084206, 0.86932987, 0.86780950, 0.86628095, 0.86474426, 0.86319942, 0.86164646, 0.86008539, 0.85851622, 0.85693898, 0.85535366, 0.85376030, 0.85215890, 0.85054948, 0.84893206, 0.84730664, 0.84567325, 0.84403190, 0.84238260, 0.84072537, 0.83906024, 0.83738720, 0.83570628, 0.83401750, 0.83232087, 0.83061640, 0.82890411, 0.82718403, 0.82545615, 0.82372051, 0.82197712, 0.82022598, 0.81846713, 0.81670057, 0.81492633, 0.81314441, 0.81135485, 0.80955764, 0.80775282, 0.80594039, 0.80412038, 0.80229280, 0.80045766, 0.79861499, 0.79676481, 0.79490713, 0.79304196, 0.79116933, 0.78928925, 0.78740175, 0.78550683, 0.78360452, 0.78169483, 0.77977779, 0.77785340, 0.77592170, 0.77398269, 0.77203640, 0.77008284, 0.76812203, 0.76615399, 0.76417874, 0.76219630, 0.76020668, 0.75820991, 0.75620600, 0.75419498, 0.75217685, 0.75015165, 0.74811938, 0.74608007, 0.74403374, 0.74198041, 0.73992010, 0.73785281, 0.73577859, 0.73369744, 0.73160938, 0.72951444, 0.72741263, 0.72530397, 0.72318849, 0.72106620, 0.71893712, 0.71680128, 0.71465869, 0.71250937, 0.71035335, 0.70819064, 0.70602126, 0.70384524, 0.70166259, 0.69947334, 0.69727751, 0.69507511, 0.69286617, 0.69065071, 0.68842875, 0.68620031, 0.68396541, 0.68172407, 0.67947632, 0.67722217, 0.67496165, 0.67269477, 0.67042156, 0.66814204, 0.66585623, 0.66356416, 0.66126584, 0.65896129, 0.65665055, 0.65433362, 0.65201053, 0.64968131, 0.64734597, 0.64500454, 0.64265703, 0.64030348, 0.63794390, 0.63557832, 0.63320676, 0.63082923, 0.62844577, 0.62605639, 0.62366112, 0.62125998, 0.61885299, 0.61644017, 0.61402156, 0.61159716, 0.60916701, 0.60673113, 0.60428953, 0.60184225, 0.59938930, 0.59693071, 0.59446650, 0.59199669, 0.58952132, 0.58704039, 0.58455394, 0.58206199, 0.57956456, 0.57706167, 0.57455336, 0.57203963, 0.56952052, 0.56699605, 0.56446624, 0.56193112, 0.55939071, 0.55684504, 0.55429412, 0.55173799, 0.54917666, 0.54661017, 0.54403853, 0.54146177, 0.53887991, 0.53629298, 0.53370100, 0.53110400, 0.52850200, 0.52589503, 0.52328310, 0.52066625, 0.51804450, 0.51541788, 0.51278640, 0.51015010, 0.50750899, 0.50486311, 0.50221247, 0.49955711, 0.49689705, 0.49423231, 0.49156292, 0.48888890, 0.48621028, 0.48352708, 0.48083933, 0.47814706, 0.47545028, 0.47274903, 0.47004333, 0.46733321, 0.46461869, 0.46189979, 0.45917655, 0.45644898, 0.45371712, 0.45098099, 0.44824061, 0.44549602, 0.44274723, 0.43999427, 0.43723717, 0.43447596, 0.43171066, 0.42894129, 0.42616789, 0.42339047, 0.42060907, 0.41782372, 0.41503442, 0.41224123, 0.40944415, 0.40664322, 0.40383846, 0.40102990, 0.39821756, 0.39540148, 0.39258167, 0.38975817, 0.38693101, 0.38410020, 0.38126577, 0.37842775, 0.37558618, 0.37274107, 0.36989245, 0.36704035, 0.36418479, 0.36132581, 0.35846342, 0.35559766, 0.35272856, 0.34985613, 0.34698041, 0.34410143, 0.34121920, 0.33833377, 0.33544515, 0.33255337, 0.32965846, 0.32676045, 0.32385937, 0.32095523, 0.31804808, 0.31513793, 0.31222481, 0.30930876, 0.30638980, 0.30346795, 0.30054324, 0.29761571, 0.29468537, 0.29175226, 0.28881641, 0.28587783, 0.28293657, 0.27999264, 0.27704608, 0.27409691, 0.27114516, 0.26819086, 0.26523403, 0.26227471, 0.25931292, 0.25634868, 0.25338204, 0.25041301, 0.24744162, 0.24446790, 0.24149189, 0.23851359, 0.23553306, 0.23255031, 0.22956537, 0.22657826, 0.22358903, 0.22059769, 0.21760427, 0.21460881, 0.21161133, 0.20861185, 0.20561041, 0.20260704, 0.19960176, 0.19659460, 0.19358559, 0.19057475, 0.18756213, 0.18454774, 0.18153161, 0.17851377, 0.17549425, 0.17247308, 0.16945029, 0.16642590, 0.16339995, 0.16037246, 0.15734346, 0.15431297, 0.15128104, 0.14824768, 0.14521292, 0.14217680, 0.13913934, 0.13610058, 0.13306053, 0.13001922, 0.12697670, 0.12393298, 0.12088809, 0.11784206, 0.11479493, 0.11174671, 0.10869744, 0.10564715, 0.10259587, 0.09954362, 0.09649043, 0.09343634, 0.09038136, 0.08732554, 0.08426889, 0.08121145, 0.07815324, 0.07509430, 0.07203465, 0.06897433, 0.06591335, 0.06285176, 0.05978957, 0.05672682, 0.05366354, 0.05059975, 0.04753548, 0.04447077, 0.04140564, 0.03834012, 0.03527424, 0.03220803, 0.02914151, 0.02607472, 0.02300768, 0.01994043, 0.01687299, 0.01380539, 0.01073766, 0.00766983, 0.00460193, 0.00153398}; inline void FFT() { // 1024 отсчета int32_t i,i1,j,k,l,l1,l2; float c1,c2,tx,ty,t1,t2,u1,u2,z; /* Do the bit reversal */ j = 0; for (i = 0; i < 1023; i++) { if (i < j) { tx = x[i]; ty = y[i]; x[i] = x[j]; y[i] = y[j]; x[j] = tx; y[j] = ty; } k = 512; while (k <= j) { j -= k; k >>= 1; } j += k; } /* Compute the FFT */ c1 = -1.0; c2 = 0.0; l2 = 1; for (l = 0; l < 10; l++) { l1 = l2; l2 <<= 1; u1 = 1.0; u2 = 0.0; for (j = 0; j < l1; j++) { for (i = j; i < 1024; i += l2) { i1 = i + l1; t1 = u1 * x[i1] - u2 * y[i1]; t2 = u1 * y[i1] + u2 * x[i1]; x[i1] = x[i] - t1; y[i1] = y[i] - t2; x[i] += t1; y[i] += t2; } z = u1 * c1 - u2 * c2; u2 = u1 * c2 + u2 * c1; u1 = z; } c2 = -sqrt((1.0 - c1) / 2.0); c1 = sqrt((1.0 + c1) / 2.0); } /* Calculate the absolute value */ // for (i = 0; i < 1024; i++) { // y[i] = (y[i]*y[i] + x[i]*x[i]); // /1024; // } } void DMA_init(void) { RCC_BASE->AHBENR |= 1; //DMA1EN - вкл.тактирования DMA1, все остальное (таймер, порты, ADC, RCC_PCLK2_Div6) stm32duino по умолчанию уже включило // DMA1->CCR1: MEM2MEM=0, PL=2, MSIZE=01, PSIZE=01, MINC=1, PINC=0, CIRC=0, DIR=0, TEIE=0, HTIE=0, TCIE=0, EN=1 DMA1_BASE->CCR1 |= 0x2000; // PL=2 (bits 13-12) - high DMA1_BASE->CCR1 |= 0x0400; // MSIZE=01 (bits 11-10) - 16 bits DMA1_BASE->CCR1 |= 0x0100; // PIZE=01 (bits 9-8) - 16 bits DMA1_BASE->CCR1 |= 0x0080; // MINC (bit 7) инкрементировать адреса в памяти DMA1_BASE->CNDTR1 = size; // сколько сэмплов передать (read only when channel disabled) DMA1_BASE->CPAR1 = (uint32_t)&ADC1_BASE->DR; // читать из ADC1 DMA1_BASE->CMAR1 = (uint32_t)buffADC; // куда писать DMA1_BASE->CCR1 |= 1; // EN (bit 0) 0 включаем } void ADC_init() { int a = analogRead(AUDIO_INPUT); // ADC1->CR1: AWDEN=0, JAWDEN=0, DUALMOD=0(Independed), DISCNUM=0, JDISCEN=0, DISCEN=0, JAUTO=0, AWDSGL=0, SCAN=0, JEOCIE=0, AWDIE=0, EOCIE=0, AWDCH=0 // ADC1->CR2: TSVREFE=0, SWSTART=0, JSWSTART=0, EXTTRIG=1, EXTCEL=3(Tim2-CC2), JEXTTRIG=0, JEXTCEL=0, ALIGN=0, DMA=1, RSTCAL=0, CAL=0, CONT=0, ADON=1 // ADC1->SQR1.l=0 (one channel), SQR3.SQ1=0 (first channel) ADC1_BASE->CR2 |= 0x00100000; // EXTTRIG (bit 20) //////// ADC1_BASE->CR2 |= 0x00000002; // CONT=1 (bit 1) - это непрерывное преобразование без таймера ??? ADC1_BASE->CR2 &= 0xfff1ffff; // ADC1_BASE->CR2 |= 0x00060000; // EXTCEL=3 (bit 19-17) Tim2CC2 ADC1_BASE->CR2 |= 0x000a0000; // EXTCEL=5 (bit 19-17) Tim4CC4 ADC1_BASE->CR2 |= 0x00000100; // DMA (bit 8) ADC1_BASE->CR2 |= 1; // ADON } void TIMER2_init() { // channel 2 - 4 MHz for filter // default by stm32duino: TIMER2->CR1.ARPE=1, CR1.CEN=1, CCMR1/2=[0x68], DMAR=0x81, EGR.CC2G=1, EGR.UG=1 (?), CCER.CC2E=1 TIMER2_BASE->ARR = dividorFilter[stateF]; // 8; // 36000k/9=4000k - верхняя частота для фильтра TIMER2_BASE->CCR2 = (dividorFilter[stateF] + 1)/2; // 4; // 8/2=4 TIMER2_BASE->CCER = 0x0010; // CC2 enable } void TIMER4_init() { // channel 4 - 48 kHz to ADC // default by stm32duino: TIMER4->CR1.ARPE=1, CR1.CEN=1, CCMR1/2=[0x68], DMAR=0x81, EGR.CC2G=1, EGR.UG=1 (?), CCER.CC2E=1 TIMER4_BASE->ARR = dividorADC[stateA]; // 251; //// 749; // 36000k/750=48k == делитель=756, частота 47.619 - чтобы не было разностных частот (16к) TIMER4_BASE->CCR4 = (dividorADC[stateA] + 1)/2; // 126; // 374; // 750/2=375 TIMER4_BASE->CCER = 0x1000; // CC4 enable } void TIMER1_init() { // настраиваем для использования энкодера на ногах PA8 и PA9 TIMER1_BASE->CR1 = 0; // Временно выключаем таймер TIMER1_BASE->SMCR = TIMER_SMCR_SMS_ENCODER2; TIMER1_BASE->CCMR1 = TIMER_CCMR1_IC1F | TIMER_CCMR1_IC2F; TIMER1_BASE->CCMR1 |= TIMER_CCMR1_CC1S_INPUT_TI1 | TIMER_CCMR1_CC2S_INPUT_TI2; TIMER1_BASE->CCMR2 = 0; ///???/// TIMER1_BASE->CCER = 0; TIMER1_BASE->CNT = 4; TIMER1_BASE->PSC = 0; TIMER1_BASE->ARR = 9; TIMER1_BASE->CCR1 = 9; TIMER1_BASE->CCR2 = 9; TIMER1_BASE->CCR3 = 0; TIMER1_BASE->CCR4 = 0; TIMER1_BASE->DMAR = 0; TIMER1_BASE->CR1 = 1; // Включаем } void inline resetDMA(){ DMA1_BASE->CCR1 &= 0xfffffffe; // EN (bit 0) DMA1_BASE->CNDTR1 = size; DMA1_BASE->IFCR = 0x0000000f; int16_t * tmp = buffFFT; buffFFT = buffADC; buffADC = tmp; DMA1_BASE->CMAR1 = (uint32_t)buffADC; // куда писать DMA1_BASE->CCR1 |= 1; } union ct { float f; uint32_t d; }; int getLog6(float b) { ct c; c.f = b; if ((c.d & 0x007fffff) >= 6558192) return ((c.d >> 23) & 0xff)*6 + 5; else if((c.d & 0x007fffff) >= 4927477) return ((c.d >> 23) & 0xff)*6 + 4; else if((c.d & 0x007fffff) >= 3474675) return ((c.d >> 23) & 0xff)*6 + 3; else if((c.d & 0x007fffff) >= 2180375) return ((c.d >> 23) & 0xff)*6 + 2; else if((c.d & 0x007fffff) >= 1027286) return ((c.d >> 23) & 0xff)*6 + 1; else return ((c.d >> 23) & 0xff)*6; } #define fontdatatype uint8_t extern fontdatatype SmallFont[]; /*void writeLetter(uint16_t x, uint16_t y, uint16_t color, uint8_t ch) { Address_set(x, y, x+7, y+4); int offs = (ch-32)*5 + 4; for(int j = 0; j < 5; j++) { for(int i = 0; i < 8; i++) { if(SmallFont[offs+j] & (1 << i)) Lcd_Write_Pixel(color); else Lcd_Write_Pixel(0); } } }*/ void writeLetter_x2(uint32_t x, uint32_t y, uint16_t color, uint8_t ch) { Address_set(x, y, x+15, y+9); int offs = (ch-32)*5 + 4; for(int j = 0; j < 5; j++) { for(int k = 0; k < 2; k++) for(int i = 0; i < 8; i++) { if(SmallFont[offs+j] & (1 << i)) { Lcd_Write_Pixel(color); Lcd_Write_Pixel(color); } else { Lcd_Write_Pixel(0); Lcd_Write_Pixel(0); } } } } void writeNumber_2(int y, int n) { // написать 1-2-значное число n с центром по координате y if(n < 10) { writeLetter_x2(303, y - 4, 0xffff, 48 + n); } else { writeLetter_x2(303, y - 12, 0xffff, 48 + n/10); writeLetter_x2(303, y + 0, 0xffff, 48 + n%10); } } void putScale() { Address_set(300, 0, 319, 479); for (int i = 0; i < 20*480; i++) { Lcd_Write_Pixel(0); } V_line(300, 0, 480, 0xffff); for(int i = 0; i <= maxScale[stateA]; i++) { H_line(300, i*screenScale[stateA]/1024 /*512.0/m*/, 4, 0xffff); if((i > 0) && ((i % stepScale[stateA]) == 0)) { writeNumber_2(i*screenScale[stateA]/1024 /*512.0/m*/, i); } } } #define BLUE_LO 0x00f8 #define LIGHT_BLUE_LO 0x04f8 #define RED_HI 0x07c0 #define LIGHT_RED_LO 0x0400 int screenX, screenY; void H_line_b(int x, int y, int s, uint16_t color) { // рисование синей линии с полосками каждый 12-й пиксель (считая через 3 дБ по 2 пикселя на 0.5 дБ) int s0 = s/6; // (0, i-1, s*2, 0x001f) int s1 = s%6; Address_set(x, y, x+s*2-1, y); for(uint32_t i = 0; i < s0; i++) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); // (001f >> 5) = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | LIGHT_BLUE_LO; //((c << 3) & DATA_MASK); // (001f << 3) = 00f8 GPIOB_BASE->BSRR = LCD_WR; } if (s1 > 0) for(uint32_t i = 0; i < s1; i++) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | ((c >> 5) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | BLUE_LO; //((c << 3) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; } } void H_line_r(int x, int y, int s, uint16_t color) { // рисование синей линии с полосками каждый 12-й пиксель (считая через 3 дБ по 2 пикселя на 0.5 дБ) int s0 = s/6; // (0, i-1, s*2, 0xf800) int s1 = s%6; Address_set(x*2, y, x*2+s*2-1, y); if (s1 > 1) for(uint32_t i = 1; i < s1; i++) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; } if (s1 > 0) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | LIGHT_RED_LO; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; } for(uint32_t i = 0; i < s0; i++) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; // | ((c >> 5) & DATA_MASK); // (f800 >> 5) = 07c0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | LIGHT_RED_LO; // | 0xf8; //((c << 3) & DATA_MASK); // (f800 << 3) = 7c000, X & MASK = 0 GPIOB_BASE->BSRR = LCD_WR; } } void touchScreen() { // PB0, PB1 - всегда INPUT, X- и X+ поменять местами /* pinMode(PB13, INPUT); // Y+ A2 : RS - PB13 connect to PB0 pinMode( PB3, INPUT); // Y- 8 : D0 - PB3 pinMode(PB4, OUTPUT); // X+ PB4 digitalWrite(PB4, HIGH); // X+ 9 : D1 - PB4 pinMode(PB12, OUTPUT); // X- A3 : CS - PB12 connect to PB1 digitalWrite(PB12, LOW); // X- PB12 connect to PB1 screenX = analogRead(PB0); pinMode( PB4, INPUT); // X+ PB4 pinMode(PB12, INPUT); // X- PB12 connect to PB1 pinMode(PB13, OUTPUT); // Y+ PB13 digitalWrite(PB13, HIGH); // Y+ PB13 pinMode(PB3, OUTPUT); // Y- PB3 digitalWrite(PB3, LOW); // Y- PB3 screenY = analogRead(PB1); pinMode( PB4, OUTPUT); // X+ PB4 pinMode(PB12, OUTPUT); // X- PB12 connect to PB1 digitalWrite(PB12, HIGH); // X- PB12 connect to PB1 */ } void checkEncoder() { static int32_t oldEncoder = 15; int32_t encoder = (TIMER1_BASE->CNT)/2; if(encoder != oldEncoder) { Serial.print("Encoder "); Serial.println(encoder); oldEncoder = encoder; int s = stateA; stateA = encoder; if (stateA < 0) stateA = 0; if(stateA > 4) stateA = 4; if(stateA != s) { TIMER4_init(); // частота дискретизации TIMER2_init(); // управление фильтром putScale(); // (24); Serial.print("State "); Serial.println(stateA); } } } inline void checkSerialInput() { if(Serial.available()){ char ch = Serial.read(); if (ch >= '0' && ch <= '4') stateA = ch - '0'; TIMER4_init(); // частота дискретизации TIMER2_init(); // управление фильтром putScale(); // (24); Serial.print(stateA); Serial.print(' '); Serial.print(stateF); Serial.println(" OK!"); } } void setup() { Serial.begin(9600); while (!Serial) { ; } // wait for serial port to connect. GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; // все управляющие HIGH GPIOB_BASE->CRL = 0x33333433; // пины PB7-PB3, PB1-PB0 - выход, PB2 - вход (не исп.) GPIOB_BASE->CRH = 0x44433333; // пины PB12-PB8 - выход, PB15-PB13 - не исп. GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; // GPIOA_BASE->CRL = 0x4b4444b0; // пины PA7-PA2 - вход (не исп.), PA0 - аналоговый вход, PA1 - выход таймера (для отладки) Lcd_Init(); buffADC = ADCBuffer; buffFFT = ADCBuffer2; DMA_init(); ADC_init(); TIMER4_init(); // частота дискретизации TIMER2_init(); // управление фильтром TIMER1_init(); // энкодер for(int i = 0; i < size; i++) { buffADC[i] = 0; buffFFT[i] = 0; } putScale(); // (24); } void loop() { while((DMA1_BASE->ISR & 2)== 0) { ; } int t0 = millis(); touchScreen(); ADC_init(); resetDMA(); TIMER4_init(); for(int i = 0; i < size; i++) { x[i] = (buffFFT[i] - 2048)*w[i]; y[i] = 0; } FFT(); int ii = 0; int iii = 1320; for(int i = 1; i < 481; i++){ int32_t s = 1002 - getLog6(x[i]*x[i] + x[1024-i]*x[1024-i] + y[i]*y[i] + y[1024-i]*y[1024-i]); if (ii < s) ii = s; if (iii > s) iii = s; if(s < 0) s = 0; if(s > 150) s = 150; H_line_b(0, i-1, s, 0x001f); H_line_r(s, i-1, 150-s, 0xf800); } int t1 = millis(); static int nn = 0; if(!(nn%50)) { Serial.print(" Y: "); Serial.print(screenX); // Y+ PB13 connect to PB0 (RS - SDA) Serial.print(' '); Serial.print(" X: "); Serial.print(screenY); // X- PB12 connect to PB1 (CS - SCL) Serial.print(" dt: "); Serial.print(t1-t0); Serial.print(" min "); Serial.print(ii); Serial.print(" max "); Serial.println(iii); } nn++; if(nn > 10) checkSerialInput(); checkEncoder(); // delay(5000); }Все это собрано на макетке для Меги. Да, первоначальные варианты отлаживал на уже спаянной макетке для проекта с видеокамерой, теперь на такой же макетке собрал новую схему:
Перекинул управляющие сигналы на другие пины. В том числе и чтобы подключить энкодер: если для прерываний можно использовать любые пины, то для таймера выбора практически нет.
Незанятая панелька в левой части платы - для фильтра. Для фильтра также нужно двухполярное питание, поэтому в самой левой части платы стоит преобразователь B0505, с которого я беру -5В. Справа снизу - стабилизатор. В какой-то фабричной MIDI-плате увидел, как сделано питание:
с разъема питание - сразу на мост (чтобы переполюсовка не приводила к печальным последствиям - обычный штыревой разъем питания ведь не стандартизован: "+" может быть как на центральном, так и на внешнем контакте).
после моста - стабилизатор на 5 В.
а уже после стабилизатора на 5 В там бал преобразователь на плюс-минус 12 В - для питания операционников.
В общем, что-то подобное решил сделать здесь.
"Кроватка" фильтра пока пустая. Вставил тонкую проволочку между входом и выходом фильтра - пока отлаживаю без него.
andriano, ну если что у меня пример обработчика энкодера есть в генераторах 3.x, работает отлично, настроен так, что-б на каждый щелчок выпадать в прерывание.
dimax, во-первых, это stm32, ф не avr, а во-вторых, - энкодер работает без прерываний - аппаратно на таймере. Есть у него такой режим работы.
На avr и прерываниях у меня тоже стабильно 80 единиц за оборот (соответственно, можно 40 либо 20), там проблем нет.
andriano, вы не посмотрели что я вам написал. Версии 3.x -это на stm32, и обработчик там на таймере, именно таймер у меня организовывает прерывания, это сразу станет понятно посмотрев настройки в сетапе.
dimax, я посмотрел, но посмотрел только первый пост (на который, собственно, и указывала ссылка). Чуть позже посмотрю дальше.
А сейчас немного об ограничении частотного диапазона сигналов, подлежащих оцифровке.
Собственно, известно, что согласно теореме Котельникова-Найквиста-Шеннона в непрерывном сигнале могут быт восстановлены после оцифровки только частоты меньшие половины частоты дискретизации. А что же с теми, что выше? Они теряются? Нет, хуже! Они "отражаются" от половинной частоты дискретизации. Т.е. если частота дискретизации, скажем, 20 кГц, а в спектре была частота 13 кГц, то после оцифровки-восстановления в спектре появится частота 7 кГц, которой не было в исходном сигнале.
Это крайне нежелательно, т.к. заметность различных искажений различна. Многие виды искажений мало заметны на слух из-за эффекта маскировки. Скажем, если у нас есть частота 3 кГц сигнала сложной формы, то кроме 3 кГц в спектре присутствуют все кратные частоты: 6 кГц, 9 кГц, 12 кГц, 15 кГц, 18 кГц... В случае линейных или нелинейных искажений новые частоты у нас не появятся - останется тот же набор частот, только с немного измененным соотношением между ними. Такие искажения довольно слабо заметны на слух. А вот в случае оцифровки вместо частот 12 кГц, 15 кГц, 18 кГц у нас (частота дискретизации 20 кГц) появятся 8кГц, 5 кГц, 2 кГц, которых не было в исходном сигнале, а потому они будут весьма заметны. Кроме того, есть эффект маскировки, за счет которого частоты небольшой интенсивности могут полностью маскироваться (т.е. совсем не восприниматься слухом) более низкими частотами. Но низкие частоты не маскируются ничем. Т.е. частоты 2 (а скорее всего и 5) кГц будут сильно заметны даже при малой амплитуде.
Другими словами, во избежание существенного снижения качества звукового сигнала его перед оцифровкой необходимо фильтровать от высоких частот.
И если мы делаем спектранализатор, логично, чтобы оцифровываемый им сигнал также пропускался через ФНЧ.
О самом ФНЧ расскажу позже, а сейчас просто приведу два фото.
Это спектр прямоугольного сигнала, снятый без фильтра. Чакстота исходного сигнала 1.1 кГц, а частота дискретизации 23.8 кГц, ее половина 11.9. Сам сигнал виден в виде "осциллограммы" белым цветом поверх спектра. Кавычки потому, что "осциллограмма" нарисована не линиями, а точками. Собственно, видно, что сигнал пропущее через ФВЧ, ну да речь не о том. В спектре должны присутствовать основной тон 1.1кГц, а также его нечетные гармоники: 3.3кГц, 5.5кГц, 7.7кГц, 9.9кГц, 12.1кГц, 14.3кГц...
Но на фото видно гораздо больше. Все лишнее - это и есть зеркальные частоты.
Совсем другое дело, если пропустить сигнал через фильтр четвертого порядка с частотой среза 10.3 кГц:
Конечно что-то осталось, но уже гораздо меньше. Чудес не бывает. И, кроме того, следует отметить, что динамический диапазон этого индикатора составляет 75 дБ, при том, что динамический диапазон 12-разрядного звука лишь 74 дБ (а как мы знаем, у stm32 12-разрядный АЦП). Т.е. малейшие шумы, включая шумы дискретизации, на индикаторе будут видны.
Пасибки. Вот оказывается почему у меня не получилось оцифровав звук (речь) достоверно выделить форманты и "опознать голос". Хотели с сыном подавать команды роботу голосом, как-то сразу не задалось, а потом бросили. Сначала надо фильтровать, а потом оцифровывать, а не наоборот.. пасибки.
Ну хорошо, хоть кому-то понадобилось. А то, когда писал, очень сильно сомневался, стоит ли излагать хотя бы кусочки теории.
Конечно, и больше чем уверен что не только мне. За долгую жизнь многа раз убеждался что всегда найдется N+1 читатель, которому это точно полезно, ИМХО, не стоит таким вопросом заморачиваться даже на "тютельку". :)
Кстати! Появилась мысля применить "программный фильтр", типа нашего восстановления засветки ИК-датчика. Вы можете попробовать на своей схеме, он прост: берем эталонный сигнал (напр. прямоугольник) и смотрим величину "отражений". Как видно по вашим картинкам - отражения распространяются от максиумов (и вообще от найденных частот) по определенному закону. Вот этот "закон" и вычитаем, из спектра в порядке ранжирования максиумов (и вообще найденных частот).
Т.е. смотрим, что +-1 гребенка меньше максиума на хх%, +-2-я гребенка .. думаю достаточно. Фиксим эти проценты, и получив спектр, сортируем его копию по убыванию амплитуды частот и в порядке копии "нормируем" спектр на эти проценты.
Попробуйте, мне наши микрофонные уши сейчас не проверить. (очень не скоро).
Arhat109-2, алгоритма не понял, но, думаю, Вы и сами в нем не разобрались.
Видите-ли, при "отражении" происходит необратимая потеря информации. Следовательно, никакие попытки восстановления сигнала в принципе не могут привести к успеху.
Ну хорошо, в процессе измерений я пользовался периодическим сигналом с генератора. Там можно хоть пытаться найти какие-то закономерности (реальные или мнимые - это отдельный вопрос) А будет музыкальный сигнал с непрерывным спектром?
Насчет энкодера - выяснилась закономерность: допустим я задаю 5 позиций от 0 до 4. Если я кручу энкодер в пределах первого круга, он выдает 2 (1 - в зависимости от настроек) импульса на щелчок, а как только перехожу через границу (т.е. либо "ниже 0", либо "выше 4"), то он необратимо переходит в режим 4(2) импульса на щелчок. И побороть это мне пока не удалось.
dimax, к сожалению, воспользоваться Вашим кодом я не смог. Подозреваю, что Вы вообще нестандартно используете энкодер - лишь для того, чтобы он пропускал "лишние" импульсы, когда их 2 или 4 на щелчок, а подсчитывает положение энкодера сам камень через прерывания.
Т.е. я хочу вообще избавиться от прерываний, настроить энкодер по счету от 0 до 4 и чтобы положение энкодера я мог узнавать, читая TIMER4_BASE->CNT.
Arhat109-2, алгоритма не понял, но, думаю, Вы и сами в нем не разобрались.
Видите-ли, при "отражении" происходит необратимая потеря информации. Следовательно, никакие попытки восстановления сигнала в принципе не могут привести к успеху.
Ну хорошо, в процессе измерений я пользовался периодическим сигналом с генератора. Там можно хоть пытаться найти какие-то закономерности (реальные или мнимые - это отдельный вопрос) А будет музыкальный сигнал с непрерывным спектром?
dimax, к сожалению, воспользоваться Вашим кодом я не смог. Подозреваю, что Вы вообще нестандартно используете энкодер - лишь для того, чтобы он пропускал "лишние" импульсы, когда их 2 или 4 на щелчок, а подсчитывает положение энкодера сам камень через прерывания.Т.е. я хочу вообще избавиться от прерываний, настроить энкодер по счету от 0 до 4 и чтобы положение энкодера я мог узнавать, читая TIMER4_BASE->CNT.
А в чём проблема? Сделать ARR=65535.
TIMER4_BASE->CR1=(1<<2)|(1<<9);//CKD10 URS TIMER4_BASE->CCMR1=0xf1f1; //(1<<0)|(1<<8 );//cc1s, cc2s input mapped TI1/TI2 TIMER4_BASE->CCER=(1<<1)|(1<<5)|(1<<0)|(1<<4);//Capture/Compare 1,2 output polarity TIMER4_BASE->SMCR=(1<<1)|(1<<0);//Encoder mode3(SMS bit) TIMER4_BASE->CNT=0; // с какого числа начинать счёт TIMER4_BASE->ARR=65535;//ограничение счёта TIMER4_BASE->SR=0; TIMER4_BASE->EGR=1; TIMER4_BASE->CR1|=(1<<0);//запускА падать в прерывание по достижении %ARR% или нет -это дело хозяйское :)
dimax, мне нужно не 65535, мне нужно 4 ровно! Иначе аппаратный энкодер не будет выполнять своих функций. Можно, конечно программно вычислят остаток по модулю 5, и даже при движении "вверх" у нас не будет наступато "переполнение" и связанное с ним изменение коэффициента пересчета. Но ведь при движении "вниз" по пересечению 0 у нас этот нежелательный эффект останется.
Так что вопрос не решен.
Да, в процессе экспериментов выяснил, что в самом низкочастотном дианазоне уровень шумов примерно на 10 дБ выше, чем в остальных:
Оказалось, время, за которое вводится массив отсчетов, больше, чем время FFT, и скетч начинал передавать данные на экран, когда еще шла оцифровка сигнала. Отсюда и помеха, имеющая вид широкополосного шума.
Написал - не начинать работу с экраном, пока не закончился ввод данных. Помогло.
andriano , Мы явно не понимаем друг друга. Что вам надо то? То вы пишите "чтобы положение энкодера я мог узнавать, читая TIMER4_BASE->CNT" Я дал скетч, узнавайте через CNT. С нолём проблем нет, ставьте начало счёта на 32767, и пожалуйста, по 30 тыщ щелчков счёта в любую сторону. Читаете счётный регистр в лупе или где надо, смотрите в какую сторону крутилось, счётчик опять скидываете на 32767. Совершенно не понимаю в чём тут может быть проблема.
dimax, хотелось полностью аппаратное решение, а Вы опять предлагаете программную обработку.
Ладно, решил пока проект на этом завершить.
Пока фотографировал, понял, почему приборы делают по 20 см в глубину, хотя корпус совершенно пустой. Я сделал 5 см и уже два раза только за время фотографирования он у меня упал. В общем, первоначально думал нарисовать и распечатать заднюю стенку на 3D-ринтере, но сейчас появилось желание сделать ее из толстого металла, миллиметров так 5, чтобы неглубоктй корпус не опрокидывался вперед при малейшем воздействии, например, перемещении сигнального кабеля.
Фото с генератора:
Фото с звуковым сигналом (MP3-плеер):
Схема:
Скетч:
#include "dma.h" #include "MAX261.h" //#define DEBUG #define LCD_RD -1 // не исп. #define LCD_WR 0x1000 // PB12 было 0x4000 // PB14 #define LCD_RS 0x0800 // PB11 было 0x2000 // PB13 #define LCD_CS 0x0002 // PB1 было 0x1000 // PB12 #define LCD_RESET 0x0001 // PB0 было 0x0800 // PB11 #define DATA_MASK 0x07f8 // PB3-PB10, данные передаются в разрядах: D0-PB3, D1-PB4, ... D7-PB10 #define CTRL_MASK_DEFAULT 0x1803 // все управляющие биты HIGH (WR | RS | CS | RESET) #define CTRL_MASK_WR_RS_CS 0x0001 // маска для записи команд (RESET) #define CTRL_MASK_WR_CS 0x0801 // маска для записи данных (RS | RESET) #define AUDIO_INPUT PA0 // аналоговый вход #define size 1024 // длина буфера оцифровки float x[size], y[size]; // действительные и мнимые части. int16_t ADCBuffer[size], ADCBuffer2[size]; // буфера для поочередного ввода данных с АЦП через ПДП (другой буфер обрабатывается ЦПУ) int16_t * buffADC, * buffFFT; // адреса буферов для ADC/DMA и CPU соответственно byte stateA = 0; // частота оцифровки ADC: 0 - 11.9кГц, 1 - 23.8кГц, 3 - 47.6кГц, 4 - 95.2кГц, 5 - 142.9кГц byte stateF = 0; // частота упр.фильтром(фильтра): 0 - 0.5МГц(5.0), 1 - 1МГц(10.0), 2 - 2МГц(20.0), 3 - 4МГц(40.0), 4 - 4МГц(56.3) byte stateOsc = 0; // рисовать ли осциллограмму: 0 - нет, 1 - точками, 2 - линиями const uint16_t dividorADC[5] = {3023, 1511, 755, 377, 251}; // делитель частоты 36 МГц до частоты дискретизации const uint16_t dividorFilter[5] = {71, 35, 17, 8, 8}; // делитель частоты 36 МГц до управляющей частоты фильтра const uint8_t filterFreqCode[5] = {26, 26, 26, 26, 0}; // управляющий код фильтра (соответствует отношениям {100,100,100,100,71}) const uint8_t stepScale[5] = {1, 1, 2, 5, 10}; // с каким шагом выводить оцифровку шкалы const uint8_t maxScale[5] = {5, 10, 20, 40, 60}; // докуда оцифровывать шкалу const uint32_t screenScale[5] = {87936, 43968, 21984, 10992, 7328}; // шаг шкалы на экране в 1/1024 пикселя void Lcd_Write_Com(unsigned char d) { // вывод команды: опустить CS, RS и WR, вывести байт, поднять WR GPIOB_BASE->ODR = CTRL_MASK_WR_RS_CS | (d << 3); GPIOB_BASE->BSRR = LCD_WR; } inline void Lcd_Write_Data(unsigned char d) { // вывод данных: опустить CS и WR, вывести байт, поднять WR GPIOB_BASE->ODR = CTRL_MASK_WR_CS | (d << 3); GPIOB_BASE->BSRR = LCD_WR; } inline void Lcd_Write_Pixel(uint16_t d) { // вывод двух байтов данных из числа uint16_t GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((d & 0xff00) >> 5); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((d & 0xff) << 3); GPIOB_BASE->BSRR = LCD_WR; } inline void Address_set(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2) { // позиционирование на экране (оптимизировано) GPIOB_BASE->ODR = 0x0151; // команда 0x2a - Column Address Set, CS, RS, WR GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((x1 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((x1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((x2 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((x2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x0159; // команда 0x2b - Page Address Set GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((y1 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((y1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((y2 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((y2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x161; // команда 0x2c - Mempry Write: переключаем на режим ввода данных (пикселей) GPIOB_BASE->BSRR = LCD_WR; } void Lcd_Init(void) { // ILI9481 вызываем один раз, поэтому по скорости не оптимизируем GPIOB_BASE->BSRR = LCD_RESET; delay(5); GPIOB_BASE->BRR = LCD_RESET; delay(15); GPIOB_BASE->BSRR = LCD_RESET; delay(15); Lcd_Write_Com(0x11); delay(20); Lcd_Write_Com(0xD0); Lcd_Write_Data(0x07); Lcd_Write_Data(0x42); Lcd_Write_Data(0x18); Lcd_Write_Com(0xD1); Lcd_Write_Data(0x00); Lcd_Write_Data(0x07); Lcd_Write_Data(0x10); Lcd_Write_Com(0xD2); Lcd_Write_Data(0x01); Lcd_Write_Data(0x02); Lcd_Write_Com(0xC0); Lcd_Write_Data(0x10); Lcd_Write_Data(0x3B); Lcd_Write_Data(0x00); Lcd_Write_Data(0x02); Lcd_Write_Data(0x11); Lcd_Write_Com(0xC5); Lcd_Write_Data(0x03); Lcd_Write_Com(0x36); Lcd_Write_Data(0x0A); Lcd_Write_Com(0x3A); Lcd_Write_Data(0x55); delay(120); Lcd_Write_Com(0x29); //Display ON Lcd_Write_Com(0x2c); // Memory Write } void H_line(uint32_t x, uint32_t y, uint32_t l, uint32_t c) { // на самом деле вертикальной, т.к. экран у нас на боку Address_set(x, y, x+l-1, y); for(uint32_t i=0; i<l; i++) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((c >> 5) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((c << 3) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; } } void V_line(uint32_t x, uint32_t y, uint32_t w, uint32_t c) { // на самом деле горизонтальной, т.к. экран у нас на боку Address_set(x, y, x, y+w-1); for(uint32_t i=0; i<w; i++) { GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((c >> 5) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_CS | ((c << 3) & DATA_MASK); GPIOB_BASE->BSRR = LCD_WR; } } const float w[size] = { // массив весовых коэффициентов функции окна для БПФ 0.00153398, 0.00460193, 0.00766983, 0.01073766, 0.01380539, 0.01687299, 0.01994043, 0.02300768, 0.02607472, 0.02914151, 0.03220803, 0.03527424, 0.03834012, 0.04140564, 0.04447077, 0.04753548, 0.05059975, 0.05366354, 0.05672682, 0.05978957, 0.06285176, 0.06591335, 0.06897433, 0.07203465, 0.07509430, 0.07815324, 0.08121145, 0.08426889, 0.08732554, 0.09038136, 0.09343634, 0.09649043, 0.09954362, 0.10259587, 0.10564715, 0.10869744, 0.11174671, 0.11479493, 0.11784206, 0.12088809, 0.12393298, 0.12697670, 0.13001922, 0.13306053, 0.13610058, 0.13913934, 0.14217680, 0.14521292, 0.14824768, 0.15128104, 0.15431297, 0.15734346, 0.16037246, 0.16339995, 0.16642590, 0.16945029, 0.17247308, 0.17549425, 0.17851377, 0.18153161, 0.18454774, 0.18756213, 0.19057475, 0.19358559, 0.19659460, 0.19960176, 0.20260704, 0.20561041, 0.20861185, 0.21161133, 0.21460881, 0.21760427, 0.22059769, 0.22358903, 0.22657826, 0.22956537, 0.23255031, 0.23553306, 0.23851359, 0.24149189, 0.24446790, 0.24744162, 0.25041301, 0.25338204, 0.25634868, 0.25931292, 0.26227471, 0.26523403, 0.26819086, 0.27114516, 0.27409691, 0.27704608, 0.27999264, 0.28293657, 0.28587783, 0.28881641, 0.29175226, 0.29468537, 0.29761571, 0.30054324, 0.30346795, 0.30638980, 0.30930876, 0.31222481, 0.31513793, 0.31804808, 0.32095523, 0.32385937, 0.32676045, 0.32965846, 0.33255337, 0.33544515, 0.33833377, 0.34121920, 0.34410143, 0.34698041, 0.34985613, 0.35272856, 0.35559766, 0.35846342, 0.36132581, 0.36418479, 0.36704035, 0.36989245, 0.37274107, 0.37558618, 0.37842775, 0.38126577, 0.38410020, 0.38693101, 0.38975817, 0.39258167, 0.39540148, 0.39821756, 0.40102990, 0.40383846, 0.40664322, 0.40944415, 0.41224123, 0.41503442, 0.41782372, 0.42060907, 0.42339047, 0.42616789, 0.42894129, 0.43171066, 0.43447596, 0.43723717, 0.43999427, 0.44274723, 0.44549602, 0.44824061, 0.45098099, 0.45371712, 0.45644898, 0.45917655, 0.46189979, 0.46461869, 0.46733321, 0.47004333, 0.47274903, 0.47545028, 0.47814706, 0.48083933, 0.48352708, 0.48621028, 0.48888890, 0.49156292, 0.49423231, 0.49689705, 0.49955711, 0.50221247, 0.50486311, 0.50750899, 0.51015010, 0.51278640, 0.51541788, 0.51804450, 0.52066625, 0.52328310, 0.52589503, 0.52850200, 0.53110400, 0.53370100, 0.53629298, 0.53887991, 0.54146177, 0.54403853, 0.54661017, 0.54917666, 0.55173799, 0.55429412, 0.55684504, 0.55939071, 0.56193112, 0.56446624, 0.56699605, 0.56952052, 0.57203963, 0.57455336, 0.57706167, 0.57956456, 0.58206199, 0.58455394, 0.58704039, 0.58952132, 0.59199669, 0.59446650, 0.59693071, 0.59938930, 0.60184225, 0.60428953, 0.60673113, 0.60916701, 0.61159716, 0.61402156, 0.61644017, 0.61885299, 0.62125998, 0.62366112, 0.62605639, 0.62844577, 0.63082923, 0.63320676, 0.63557832, 0.63794390, 0.64030348, 0.64265703, 0.64500454, 0.64734597, 0.64968131, 0.65201053, 0.65433362, 0.65665055, 0.65896129, 0.66126584, 0.66356416, 0.66585623, 0.66814204, 0.67042156, 0.67269477, 0.67496165, 0.67722217, 0.67947632, 0.68172407, 0.68396541, 0.68620031, 0.68842875, 0.69065071, 0.69286617, 0.69507511, 0.69727751, 0.69947334, 0.70166259, 0.70384524, 0.70602126, 0.70819064, 0.71035335, 0.71250937, 0.71465869, 0.71680128, 0.71893712, 0.72106620, 0.72318849, 0.72530397, 0.72741263, 0.72951444, 0.73160938, 0.73369744, 0.73577859, 0.73785281, 0.73992010, 0.74198041, 0.74403374, 0.74608007, 0.74811938, 0.75015165, 0.75217685, 0.75419498, 0.75620600, 0.75820991, 0.76020668, 0.76219630, 0.76417874, 0.76615399, 0.76812203, 0.77008284, 0.77203640, 0.77398269, 0.77592170, 0.77785340, 0.77977779, 0.78169483, 0.78360452, 0.78550683, 0.78740175, 0.78928925, 0.79116933, 0.79304196, 0.79490713, 0.79676481, 0.79861499, 0.80045766, 0.80229280, 0.80412038, 0.80594039, 0.80775282, 0.80955764, 0.81135485, 0.81314441, 0.81492633, 0.81670057, 0.81846713, 0.82022598, 0.82197712, 0.82372051, 0.82545615, 0.82718403, 0.82890411, 0.83061640, 0.83232087, 0.83401750, 0.83570628, 0.83738720, 0.83906024, 0.84072537, 0.84238260, 0.84403190, 0.84567325, 0.84730664, 0.84893206, 0.85054948, 0.85215890, 0.85376030, 0.85535366, 0.85693898, 0.85851622, 0.86008539, 0.86164646, 0.86319942, 0.86474426, 0.86628095, 0.86780950, 0.86932987, 0.87084206, 0.87234606, 0.87384184, 0.87532940, 0.87680872, 0.87827979, 0.87974259, 0.88119711, 0.88264334, 0.88408126, 0.88551086, 0.88693212, 0.88834503, 0.88974959, 0.89114576, 0.89253356, 0.89391295, 0.89528392, 0.89664647, 0.89800058, 0.89934624, 0.90068343, 0.90201214, 0.90333237, 0.90464409, 0.90594730, 0.90724198, 0.90852812, 0.90980571, 0.91107473, 0.91233518, 0.91358705, 0.91483031, 0.91606497, 0.91729100, 0.91850839, 0.91971715, 0.92091724, 0.92210867, 0.92329142, 0.92446547, 0.92563083, 0.92678747, 0.92793539, 0.92907458, 0.93020502, 0.93132671, 0.93243963, 0.93354377, 0.93463913, 0.93572569, 0.93680344, 0.93787238, 0.93893248, 0.93998375, 0.94102618, 0.94205974, 0.94308444, 0.94410026, 0.94510719, 0.94610523, 0.94709437, 0.94807459, 0.94904588, 0.95000825, 0.95096167, 0.95190614, 0.95284165, 0.95376819, 0.95468575, 0.95559433, 0.95649392, 0.95738450, 0.95826607, 0.95913862, 0.96000215, 0.96085663, 0.96170208, 0.96253847, 0.96336580, 0.96418406, 0.96499325, 0.96579336, 0.96658437, 0.96736629, 0.96813910, 0.96890280, 0.96965739, 0.97040284, 0.97113916, 0.97186634, 0.97258437, 0.97329325, 0.97399296, 0.97468351, 0.97536489, 0.97603708, 0.97670009, 0.97735390, 0.97799851, 0.97863392, 0.97926012, 0.97987710, 0.98048486, 0.98108339, 0.98167269, 0.98225274, 0.98282355, 0.98338511, 0.98393741, 0.98448046, 0.98501423, 0.98553874, 0.98605396, 0.98655991, 0.98705657, 0.98754394, 0.98802202, 0.98849079, 0.98895026, 0.98940043, 0.98984128, 0.99027281, 0.99069503, 0.99110791, 0.99151147, 0.99190570, 0.99229059, 0.99266614, 0.99303235, 0.99338921, 0.99373672, 0.99407488, 0.99440368, 0.99472312, 0.99503320, 0.99533391, 0.99562526, 0.99590723, 0.99617983, 0.99644305, 0.99669690, 0.99694136, 0.99717644, 0.99740213, 0.99761844, 0.99782535, 0.99802287, 0.99821100, 0.99838974, 0.99855907, 0.99871901, 0.99886955, 0.99901069, 0.99914242, 0.99926475, 0.99937767, 0.99948119, 0.99957530, 0.99966000, 0.99973529, 0.99980117, 0.99985764, 0.99990470, 0.99994235, 0.99997059, 0.99998941, 0.99999882, 0.99999882, 0.99998941, 0.99997059, 0.99994235, 0.99990470, 0.99985764, 0.99980117, 0.99973529, 0.99966000, 0.99957530, 0.99948119, 0.99937767, 0.99926475, 0.99914242, 0.99901069, 0.99886955, 0.99871901, 0.99855907, 0.99838974, 0.99821100, 0.99802287, 0.99782535, 0.99761844, 0.99740213, 0.99717644, 0.99694136, 0.99669690, 0.99644305, 0.99617983, 0.99590723, 0.99562526, 0.99533391, 0.99503320, 0.99472312, 0.99440368, 0.99407488, 0.99373672, 0.99338921, 0.99303235, 0.99266614, 0.99229059, 0.99190570, 0.99151147, 0.99110791, 0.99069503, 0.99027281, 0.98984128, 0.98940043, 0.98895026, 0.98849079, 0.98802202, 0.98754394, 0.98705657, 0.98655991, 0.98605396, 0.98553874, 0.98501423, 0.98448046, 0.98393741, 0.98338511, 0.98282355, 0.98225274, 0.98167269, 0.98108339, 0.98048486, 0.97987710, 0.97926012, 0.97863392, 0.97799851, 0.97735390, 0.97670009, 0.97603708, 0.97536489, 0.97468351, 0.97399296, 0.97329325, 0.97258437, 0.97186634, 0.97113916, 0.97040284, 0.96965739, 0.96890280, 0.96813910, 0.96736629, 0.96658437, 0.96579336, 0.96499325, 0.96418406, 0.96336580, 0.96253847, 0.96170208, 0.96085663, 0.96000215, 0.95913862, 0.95826607, 0.95738450, 0.95649392, 0.95559433, 0.95468575, 0.95376819, 0.95284165, 0.95190614, 0.95096167, 0.95000825, 0.94904588, 0.94807459, 0.94709437, 0.94610523, 0.94510719, 0.94410026, 0.94308444, 0.94205974, 0.94102618, 0.93998375, 0.93893248, 0.93787238, 0.93680344, 0.93572569, 0.93463913, 0.93354377, 0.93243963, 0.93132671, 0.93020502, 0.92907458, 0.92793539, 0.92678747, 0.92563083, 0.92446547, 0.92329142, 0.92210867, 0.92091724, 0.91971715, 0.91850839, 0.91729100, 0.91606497, 0.91483031, 0.91358705, 0.91233518, 0.91107473, 0.90980571, 0.90852812, 0.90724198, 0.90594730, 0.90464409, 0.90333237, 0.90201214, 0.90068343, 0.89934624, 0.89800058, 0.89664647, 0.89528392, 0.89391295, 0.89253356, 0.89114576, 0.88974959, 0.88834503, 0.88693212, 0.88551086, 0.88408126, 0.88264334, 0.88119711, 0.87974259, 0.87827979, 0.87680872, 0.87532940, 0.87384184, 0.87234606, 0.87084206, 0.86932987, 0.86780950, 0.86628095, 0.86474426, 0.86319942, 0.86164646, 0.86008539, 0.85851622, 0.85693898, 0.85535366, 0.85376030, 0.85215890, 0.85054948, 0.84893206, 0.84730664, 0.84567325, 0.84403190, 0.84238260, 0.84072537, 0.83906024, 0.83738720, 0.83570628, 0.83401750, 0.83232087, 0.83061640, 0.82890411, 0.82718403, 0.82545615, 0.82372051, 0.82197712, 0.82022598, 0.81846713, 0.81670057, 0.81492633, 0.81314441, 0.81135485, 0.80955764, 0.80775282, 0.80594039, 0.80412038, 0.80229280, 0.80045766, 0.79861499, 0.79676481, 0.79490713, 0.79304196, 0.79116933, 0.78928925, 0.78740175, 0.78550683, 0.78360452, 0.78169483, 0.77977779, 0.77785340, 0.77592170, 0.77398269, 0.77203640, 0.77008284, 0.76812203, 0.76615399, 0.76417874, 0.76219630, 0.76020668, 0.75820991, 0.75620600, 0.75419498, 0.75217685, 0.75015165, 0.74811938, 0.74608007, 0.74403374, 0.74198041, 0.73992010, 0.73785281, 0.73577859, 0.73369744, 0.73160938, 0.72951444, 0.72741263, 0.72530397, 0.72318849, 0.72106620, 0.71893712, 0.71680128, 0.71465869, 0.71250937, 0.71035335, 0.70819064, 0.70602126, 0.70384524, 0.70166259, 0.69947334, 0.69727751, 0.69507511, 0.69286617, 0.69065071, 0.68842875, 0.68620031, 0.68396541, 0.68172407, 0.67947632, 0.67722217, 0.67496165, 0.67269477, 0.67042156, 0.66814204, 0.66585623, 0.66356416, 0.66126584, 0.65896129, 0.65665055, 0.65433362, 0.65201053, 0.64968131, 0.64734597, 0.64500454, 0.64265703, 0.64030348, 0.63794390, 0.63557832, 0.63320676, 0.63082923, 0.62844577, 0.62605639, 0.62366112, 0.62125998, 0.61885299, 0.61644017, 0.61402156, 0.61159716, 0.60916701, 0.60673113, 0.60428953, 0.60184225, 0.59938930, 0.59693071, 0.59446650, 0.59199669, 0.58952132, 0.58704039, 0.58455394, 0.58206199, 0.57956456, 0.57706167, 0.57455336, 0.57203963, 0.56952052, 0.56699605, 0.56446624, 0.56193112, 0.55939071, 0.55684504, 0.55429412, 0.55173799, 0.54917666, 0.54661017, 0.54403853, 0.54146177, 0.53887991, 0.53629298, 0.53370100, 0.53110400, 0.52850200, 0.52589503, 0.52328310, 0.52066625, 0.51804450, 0.51541788, 0.51278640, 0.51015010, 0.50750899, 0.50486311, 0.50221247, 0.49955711, 0.49689705, 0.49423231, 0.49156292, 0.48888890, 0.48621028, 0.48352708, 0.48083933, 0.47814706, 0.47545028, 0.47274903, 0.47004333, 0.46733321, 0.46461869, 0.46189979, 0.45917655, 0.45644898, 0.45371712, 0.45098099, 0.44824061, 0.44549602, 0.44274723, 0.43999427, 0.43723717, 0.43447596, 0.43171066, 0.42894129, 0.42616789, 0.42339047, 0.42060907, 0.41782372, 0.41503442, 0.41224123, 0.40944415, 0.40664322, 0.40383846, 0.40102990, 0.39821756, 0.39540148, 0.39258167, 0.38975817, 0.38693101, 0.38410020, 0.38126577, 0.37842775, 0.37558618, 0.37274107, 0.36989245, 0.36704035, 0.36418479, 0.36132581, 0.35846342, 0.35559766, 0.35272856, 0.34985613, 0.34698041, 0.34410143, 0.34121920, 0.33833377, 0.33544515, 0.33255337, 0.32965846, 0.32676045, 0.32385937, 0.32095523, 0.31804808, 0.31513793, 0.31222481, 0.30930876, 0.30638980, 0.30346795, 0.30054324, 0.29761571, 0.29468537, 0.29175226, 0.28881641, 0.28587783, 0.28293657, 0.27999264, 0.27704608, 0.27409691, 0.27114516, 0.26819086, 0.26523403, 0.26227471, 0.25931292, 0.25634868, 0.25338204, 0.25041301, 0.24744162, 0.24446790, 0.24149189, 0.23851359, 0.23553306, 0.23255031, 0.22956537, 0.22657826, 0.22358903, 0.22059769, 0.21760427, 0.21460881, 0.21161133, 0.20861185, 0.20561041, 0.20260704, 0.19960176, 0.19659460, 0.19358559, 0.19057475, 0.18756213, 0.18454774, 0.18153161, 0.17851377, 0.17549425, 0.17247308, 0.16945029, 0.16642590, 0.16339995, 0.16037246, 0.15734346, 0.15431297, 0.15128104, 0.14824768, 0.14521292, 0.14217680, 0.13913934, 0.13610058, 0.13306053, 0.13001922, 0.12697670, 0.12393298, 0.12088809, 0.11784206, 0.11479493, 0.11174671, 0.10869744, 0.10564715, 0.10259587, 0.09954362, 0.09649043, 0.09343634, 0.09038136, 0.08732554, 0.08426889, 0.08121145, 0.07815324, 0.07509430, 0.07203465, 0.06897433, 0.06591335, 0.06285176, 0.05978957, 0.05672682, 0.05366354, 0.05059975, 0.04753548, 0.04447077, 0.04140564, 0.03834012, 0.03527424, 0.03220803, 0.02914151, 0.02607472, 0.02300768, 0.01994043, 0.01687299, 0.01380539, 0.01073766, 0.00766983, 0.00460193, 0.00153398}; inline void FFT() { // 1024 отсчета int32_t i,i1,j,k,l,l1,l2; float c1,c2,tx,ty,t1,t2,u1,u2,z; /* Do the bit reversal */ j = 0; for (i = 0; i < 1023; i++) { if (i < j) { tx = x[i]; ty = y[i]; x[i] = x[j]; y[i] = y[j]; x[j] = tx; y[j] = ty; } k = 512; while (k <= j) { j -= k; k >>= 1; } j += k; } /* Compute the FFT */ c1 = -1.0; c2 = 0.0; l2 = 1; for (l = 0; l < 10; l++) { l1 = l2; l2 <<= 1; u1 = 1.0; u2 = 0.0; for (j = 0; j < l1; j++) { for (i = j; i < 1024; i += l2) { i1 = i + l1; t1 = u1 * x[i1] - u2 * y[i1]; t2 = u1 * y[i1] + u2 * x[i1]; x[i1] = x[i] - t1; y[i1] = y[i] - t2; x[i] += t1; y[i] += t2; } z = u1 * c1 - u2 * c2; u2 = u1 * c2 + u2 * c1; u1 = z; } c2 = -sqrt((1.0 - c1) / 2.0); c1 = sqrt((1.0 + c1) / 2.0); } /* Calculate the absolute value */ // for (i = 0; i < 1024; i++) { // y[i] = (y[i]*y[i] + x[i]*x[i]); // /1024; // } } void DMA_init(void) { RCC_BASE->AHBENR |= 1; //DMA1EN - вкл.тактирования DMA1, все остальное (таймер, порты, ADC, RCC_PCLK2_Div6) stm32duino по умолчанию уже включило // DMA1->CCR1: MEM2MEM=0, PL=2, MSIZE=01, PSIZE=01, MINC=1, PINC=0, CIRC=0, DIR=0, TEIE=0, HTIE=0, TCIE=0, EN=1 DMA1_BASE->CCR1 |= 0x2000; // PL=2 (bits 13-12) - high DMA1_BASE->CCR1 |= 0x0400; // MSIZE=01 (bits 11-10) - 16 bits DMA1_BASE->CCR1 |= 0x0100; // PIZE=01 (bits 9-8) - 16 bits DMA1_BASE->CCR1 |= 0x0080; // MINC (bit 7) инкрементировать адреса в памяти DMA1_BASE->CNDTR1 = size; // сколько сэмплов передать (read only when channel disabled) DMA1_BASE->CPAR1 = (uint32_t)&ADC1_BASE->DR; // читать из ADC1 DMA1_BASE->CMAR1 = (uint32_t)buffADC; // куда писать DMA1_BASE->CCR1 |= 1; // EN (bit 0) 0 включаем } void ADC_init() { int a = analogRead(AUDIO_INPUT); // на всякий случай, чтобы гарантированно провести калибровку АЦП // ADC1->CR1: AWDEN=0, JAWDEN=0, DUALMOD=0(Independed), DISCNUM=0, JDISCEN=0, DISCEN=0, JAUTO=0, AWDSGL=0, SCAN=0, JEOCIE=0, AWDIE=0, EOCIE=0, AWDCH=0 // ADC1->CR2: TSVREFE=0, SWSTART=0, JSWSTART=0, EXTTRIG=1, EXTCEL=3(Tim2-CC2), JEXTTRIG=0, JEXTCEL=0, ALIGN=0, DMA=1, RSTCAL=0, CAL=0, CONT=0, ADON=1 // ADC1->SQR1.l=0 (one channel), SQR3.SQ1=0 (first channel) ADC1_BASE->CR2 |= 0x00100000; // EXTTRIG (bit 20) //////// ADC1_BASE->CR2 |= 0x00000002; // CONT=1 (bit 1) - это непрерывное преобразование без таймера ??? ADC1_BASE->CR2 &= 0xfff1ffff; // ADC1_BASE->CR2 |= 0x00060000; // EXTCEL=3 (bit 19-17) Tim2CC2 ADC1_BASE->CR2 |= 0x000a0000; // EXTCEL=5 (bit 19-17) Tim4CC4 ADC1_BASE->CR2 |= 0x00000100; // DMA (bit 8) ADC1_BASE->CR2 |= 1; // ADON } void TIMER2_init() { // channel 2 - 4 MHz for filter // default by stm32duino: TIMER2->CR1.ARPE=1, CR1.CEN=1, CCMR1/2=[0x68], DMAR=0x81, EGR.CC2G=1, EGR.UG=1 (?), CCER.CC2E=1 TIMER2_BASE->ARR = dividorFilter[stateF]; // 8; // 36000k/9=4000k - верхняя частота для фильтра TIMER2_BASE->CCR2 = (dividorFilter[stateF] + 1)/2; // 4; // 8/2=4 TIMER2_BASE->CCER = 0x0010; // CC2 enable #ifdef DEBUG Serial.print("Timer2 "); Serial.println(36000.0/(TIMER2_BASE->ARR + 1)); #endif } void TIMER4_init() { // channel 4 - 48 kHz to ADC // default by stm32duino: TIMER4->CR1.ARPE=1, CR1.CEN=1, CCMR1/2=[0x68], DMAR=0x81, EGR.CC2G=1, EGR.UG=1 (?), CCER.CC2E=1 TIMER4_BASE->ARR = dividorADC[stateA]; // 251; //// 749; // 36000k/750=48k == делитель=756, частота 47.619 - чтобы не было разностных частот (16к) TIMER4_BASE->CCR4 = (dividorADC[stateA] + 1)/2; // 126; // 374; // 750/2=375 TIMER4_BASE->CCER = 0x1000; // CC4 enable #ifdef DEBUG Serial.print("Timer4 "); Serial.println(36000.0/(TIMER4_BASE->ARR + 1)); #endif } void TIMER1_init() { // настраиваем для использования энкодера на ногах PA8 и PA9 TIMER1_BASE->CR1 = 0; // Временно выключаем таймер // TIMER1_BASE->CR1 = 0x0204; // CKD=01 TIMER1_BASE->SMCR = 0x0001; // TIMER_SMCR_SMS_ENCODER2; // TIMER4_BASE->EGR=1; // Reinitialize the counter TIMER1_BASE->CCMR1 = 0x0101; // TIMER_CCMR1_IC1F | TIMER_CCMR1_IC2F | TIMER_CCMR1_CC1S_INPUT_TI1 | TIMER_CCMR1_CC2S_INPUT_TI2; // TIMER1_BASE->CCMR1 |= TIMER_CCMR1_CC1S_INPUT_TI1 | TIMER_CCMR1_CC2S_INPUT_TI2; TIMER1_BASE->CCMR2 = 0; ///???/// TIMER1_BASE->CCER = 0; TIMER1_BASE->CNT = stateA*2; TIMER1_BASE->PSC = 0; TIMER1_BASE->ARR = 9; TIMER1_BASE->CCR1 = 0; TIMER1_BASE->CCR2 = 0; TIMER1_BASE->CCR3 = 0; TIMER1_BASE->CCR4 = 0; TIMER1_BASE->DMAR = 0; TIMER1_BASE->CR1 |= 1; // Включаем таймер(0x0001), DIR(0x0010) - бит направления вращения } void inline resetDMA(){ DMA1_BASE->CCR1 &= 0xfffffffe; // EN (bit 0) DMA1_BASE->CNDTR1 = size; DMA1_BASE->IFCR = 0x0000000f; int16_t * tmp = buffFFT; buffFFT = buffADC; buffADC = tmp; DMA1_BASE->CMAR1 = (uint32_t)buffADC; // куда писать DMA1_BASE->CCR1 |= 1; } union ct { float f; uint32_t d; }; int getLog6(float b) { ct c; c.f = b; if ((c.d & 0x007fffff) >= 6558192) return ((c.d >> 23) & 0xff)*6 + 5; else if((c.d & 0x007fffff) >= 4927477) return ((c.d >> 23) & 0xff)*6 + 4; else if((c.d & 0x007fffff) >= 3474675) return ((c.d >> 23) & 0xff)*6 + 3; else if((c.d & 0x007fffff) >= 2180375) return ((c.d >> 23) & 0xff)*6 + 2; else if((c.d & 0x007fffff) >= 1027286) return ((c.d >> 23) & 0xff)*6 + 1; else return ((c.d >> 23) & 0xff)*6; } #define fontdatatype uint8_t extern fontdatatype SmallFont[]; void writeLetter_x2(uint32_t x, uint32_t y, uint16_t color, uint8_t ch) { Address_set(x, y, x+15, y+9); int offs = (ch-32)*5 + 4; for(int j = 0; j < 5; j++) { for(int k = 0; k < 2; k++) for(int i = 0; i < 8; i++) { if(SmallFont[offs+j] & (1 << i)) { Lcd_Write_Pixel(color); Lcd_Write_Pixel(color); } else { Lcd_Write_Pixel(0); Lcd_Write_Pixel(0); } } } } void writeNumber_2(int y, int n) { // написать 1-2-значное число n с центром по координате y if(n < 10) { writeLetter_x2(303, y - 4, 0xffff, 48 + n); } else { writeLetter_x2(303, y - 12, 0xffff, 48 + n/10); writeLetter_x2(303, y + 0, 0xffff, 48 + n%10); } } void putScale() { Address_set(300, 0, 319, 479); for (int i = 0; i < 20*480; i++) { Lcd_Write_Pixel(0); } V_line(300, 0, 480, 0xffff); for(int i = 0; i <= maxScale[stateA]; i++) { H_line(300, i*screenScale[stateA]/1024 /*512.0/m*/, 4, 0xffff); if((i > 0) && ((i % stepScale[stateA]) == 0)) { writeNumber_2(i*screenScale[stateA]/1024 /*512.0/m*/, i); } } } #define BLUE_LO 0x00f8 #define LIGHT_BLUE_LO 0x04f8 #define RED_HI 0x07c0 #define LIGHT_RED_LO 0x0400 int screenX, screenY; #define TWO_PIXELS(x,y) \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS; \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS | x; \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS; \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS | y; \ GPIOB_BASE->BSRR = LCD_WR; #define TWO_PIXELS_RED \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS; \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS; \ GPIOB_BASE->BSRR = LCD_WR; #define TWO_PIXELS_LIGHT_RED \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS; \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS | RED_HI; \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_CS | LIGHT_RED_LO; \ GPIOB_BASE->BSRR = LCD_WR; void H_line_b(int x, int y, int s) { // рисование синей линии с полосками каждый 12-й пиксель (считая через 3 дБ по 2 пикселя на 0.5 дБ) int s0 = s/6; // (0, i-1, s*2, 0x001f) int s1 = s%6; Address_set(x, y, x+s*2-1, y); for(uint32_t i = 0; i < s0; i++) { TWO_PIXELS(BLUE_LO, BLUE_LO) TWO_PIXELS(BLUE_LO, BLUE_LO) TWO_PIXELS(BLUE_LO, BLUE_LO) TWO_PIXELS(BLUE_LO, BLUE_LO) TWO_PIXELS(BLUE_LO, BLUE_LO) TWO_PIXELS(BLUE_LO, LIGHT_BLUE_LO) } if (s1 > 0) for(uint32_t i = 0; i < s1; i++) { TWO_PIXELS(BLUE_LO, BLUE_LO) } } void H_line_r(int x, int y, int s) { // рисование красной линии с полосками каждый 12-й пиксель (считая через 3 дБ по 2 пикселя на 0.5 дБ) int s0 = s/6; // (0, i-1, s*2, 0xf800) int s1 = s%6; Address_set(x*2, y, x*2+s*2-1, y); if (s1 > 1) for(uint32_t i = 1; i < s1; i++) { TWO_PIXELS_RED } if (s1 > 0) { TWO_PIXELS_LIGHT_RED } for(uint32_t i = 0; i < s0; i++) { TWO_PIXELS_RED TWO_PIXELS_RED TWO_PIXELS_RED TWO_PIXELS_RED TWO_PIXELS_RED TWO_PIXELS_LIGHT_RED } } /*void setCurrentADC() { TIMER4_init(); // частота дискретизации putScale(); // (24); }*/ /*void setCurrentFilter() { TIMER2_init(); // управление фильтром Filter_SetCoeff(0, filterFreqCode[stateF]); // фильтр А, коэффициент частоты 0 (f = f0*71.09) Filter_SetCoeff(1, filterFreqCode[stateF]); // фильтр B, коэффициент частоты 0 (f = f0*71.09) }*/ void printCurrentState() { Serial.print("StateA: "); Serial.print(stateA); Serial.print(", StateF: "); Serial.println(stateF); } void checkEncoder() { // // проверка состояния энкодера и переключение режимов static int32_t oldEncoder = 15; int32_t encoder = (TIMER1_BASE->CNT)/2; if(encoder != oldEncoder) { oldEncoder = encoder; int s = stateA; stateA = encoder; if (stateA < 0) stateA = 0; if(stateA > 4) stateA = 4; if(stateA != s) { TIMER4_init(); // частота дискретизации putScale(); // (24); #ifdef DEBUG printCurrentState(); #endif } s = stateF; stateF = stateA; if(stateF != s) { TIMER2_init(); // управление фильтром Filter_SetCoeff(0, filterFreqCode[stateF]); // фильтр А, коэффициент частоты 0 (f = f0*71.09) Filter_SetCoeff(1, filterFreqCode[stateF]); // фильтр B, коэффициент частоты 0 (f = f0*71.09) #ifdef DEBUG printCurrentState(); #endif } } } inline void checkSerialInput() { // для отладки - консольное управление частотой дискретизации и частотой фильтра независимо if(Serial.available()){ char ch = Serial.read(); if (ch >= '0' && ch <= '4') { stateA = ch - '0'; TIMER4_init(); // частота дискретизации putScale(); // (24); } if (ch >= 'A' && ch <= 'E') { stateF = ch - 'A'; TIMER2_init(); // управление фильтром Filter_SetCoeff(0, filterFreqCode[stateF]); // фильтр А, коэффициент частоты 0 (f = f0*71.09) Filter_SetCoeff(1, filterFreqCode[stateF]); // фильтр B, коэффициент частоты 0 (f = f0*71.09) } printCurrentState(); } } void draw(int &ii, int &iii, int &startX) { // рисование на экране всего обновляемого (кроме шкалы) if(stateOsc) { // если нужна осциллограмма, пытаемся найти точку синхронизации int maxDelta = 0; // значение максимума производной int startXmax = 0; // позиция максимума функции int currMax = 0; // значение максимума функции for(int i = 0; i < 1024-480-3; i++) { if(maxDelta < (buffFFT[i+3+240] - buffFFT[i+240])) { // ищем максимум производной maxDelta = (buffFFT[i+3+240] - buffFFT[i+240]); startX = i; } if(currMax < buffFFT[i+240]) { // ищем максимум функции currMax = buffFFT[i+240]; startXmax = i; } } static bool searchMax = false; // признак - искать по максимуму функции (true) или максимуму производной (false) if(searchMax && (maxDelta > 350)) searchMax = false; // if(!searchMax && (maxDelta < 250)) searchMax = true; // переключаем режимы с гистерезисом if(searchMax) startX = startXmax; } int prev = -1; // значение в предыдущей точке, нужно только в режиме 2 (откуда рисовать отрезок прямой) for(int i = 1; i < 481; i++){ int32_t s = 1002 - getLog6(x[i]*x[i] + x[1024-i]*x[1024-i] + y[i]*y[i] + y[1024-i]*y[1024-i]); if (ii < s) ii = s; // s - логарифм в единицах, отмасштабированных к экрану if (iii > s) iii = s; if(s < 0) s = 0; if(s > 150) s = 150; H_line_b(0, i-1, s); // рисуем верхнюю и нижнюю части H_line_r(s, i-1, 150-s); // вертикального столбца соответствующими цветами if(stateOsc) { uint32_t xx = 150 + (buffFFT[i + startX] - 2048)/14; // xx - координата, соответствующая отсчету if((stateOsc == 1) || (xx == prev) || (prev == -1)){ // точечная осциллограмма (или горизонтальный фрагмент линейной) Address_set(xx, i-1, xx, i-1); Lcd_Write_Pixel(0xffff); } else { // осциллограмма линиями if (xx <= prev) { Address_set(xx, i-1, prev, i-1); for (int j = xx; j < prev; j++) { Lcd_Write_Pixel(0xfffe0); } } else { Address_set(prev, i-1, xx, i-1); for (int j = prev; j < xx; j++) { Lcd_Write_Pixel(0xfffe0); } } } prev = xx; } } } void setup() { #ifdef DEBUG Serial.begin(9600); while (!Serial) { ; } // wait for serial port to connect. #endif GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; // все управляющие HIGH GPIOB_BASE->CRL = 0x11111411; // пины PB7-PB3, PB1-PB0 - выход, PB2 - вход (не исп.) GPIOB_BASE->CRH = 0x44411111; // пины PB12-PB8 - выход, PB15-PB13 - не исп. GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; // GPIOA_BASE->CRL = 0x111111b0; // пины PA7-PA2 - выходы упр.фильтром, PA0 - аналоговый вход, PA1 - выход таймера (для фильтра) GPIOA_BASE->CRH = 0x14444444; // пин PA15 - выход упр.фильтром (WR), PA8-PA10 - вход энкодера, PA11-PA14 - не исп. Lcd_Init(); buffADC = ADCBuffer; buffFFT = ADCBuffer2; DMA_init(); ADC_init(); TIMER4_init(); // частота дискретизации TIMER2_init(); // управление фильтром TIMER1_init(); // энкодер for(int i = 0; i < size; i++) { buffADC[i] = 0; buffFFT[i] = 0; } putScale(); // (24); Filter_SetMode(0, 1); // фильтр А, режим 2 Filter_SetMode(1, 1); // фильтр В, режим 2 Filter_SetCoeff(0, filterFreqCode[stateF]); // фильтр А, коэффициент частоты 0 (f = f0*71.09) Filter_SetCoeff(1, filterFreqCode[stateF]); // фильтр B, коэффициент частоты 0 (f = f0*71.09) Filter_SetQ(0, 59); // фильтр А, коэффициент добротности 59 (Q=1.31) Filter_SetQ(1, 1); // фильтр B, коэффициент добротности 59 (Q=1.31) } void loop() { // while((DMA1_BASE->ISR & 2)== 0) { ; } int t0 = millis(); ADC_init(); resetDMA(); TIMER4_BASE->CNT = 0; for(int i = 0; i < size; i++) { x[i] = (buffFFT[i] - 2048)*w[i]; y[i] = 0; } FFT(); bool static buttonPressed = HIGH; { bool button = digitalRead(PA10); if(!button && buttonPressed) stateOsc++; if(stateOsc > 2) stateOsc = 0; buttonPressed = button; } int ii = 0; int iii = 1320; int startX = 0; while((DMA1_BASE->ISR & 2)== 0) { ; } draw(ii, iii, startX); #ifdef DEBUG int t1 = millis(); static int nn = 0; if(!(nn%200)) { Serial.print(" Y: "); Serial.print(screenX); // Y+ PB13 connect to PB0 (RS - SDA) Serial.print(' '); Serial.print(" X: "); Serial.print(screenY); // X- PB12 connect to PB1 (CS - SCL) Serial.print(" dt: "); Serial.print(t1-t0); Serial.print(" min "); Serial.print(ii); Serial.print(" max "); Serial.print(iii); Serial.print(" start "); Serial.println(startX); } nn++; if(nn > 10) checkSerialInput(); #endif checkEncoder(); }Файлы работы с фильтром, заголовочный MAX261.h:
#ifndef _MAX261_h_ #define _MAX261_h_ #include <Arduino.h> #define NOP __asm__ __volatile__ ("nop\n\t") #define nop __asm__ __volatile__ ("nop\n\t") #define Nop __asm__ __volatile__ ("nop\n\t") // фильтры адресуются по номеру% 0 - А, 1 - В void Filter_SetMode(byte channel, byte mode); // 1-4 - установка режима (используются только 1 и 2 (коды 00 и 01 соответственно)) void Filter_SetQ(byte channel, byte cQ); // установка добротности фильтра, 7 разрядов void Filter_SetCoeff(byte channel, byte cF); // установка доп. коэффициента частоты (от 100 до 200) - 6 разрядов от 0 до 63 #endifи MAX261.cpp:
#include "MAX261.h" //#define DEBUG_MAX261 #define WR_FILTER (0x8000) // WR - PA15 // A3 - PA7 // A2 - PA6 // A1 - PA5 // A0 - PA4 // D1 - PA3 // D0 - PA2 void checkFilter(uint16_t mask); #define WRITE_TO_FILTER \ GPIOA_BASE->ODR = mask; /* нужна пауза 25 мкс - меньше 2 тактов */ \ GPIOA_BASE->BRR = WR_FILTER; \ NOP; NOP; NOP; NOP; NOP; NOP; NOP; /* 7 тактов (всего нужно 250 нс - 9 тактов) */ \ GPIOA_BASE->BSRR = WR_FILTER; /* нужна пауза 10 мкс - менее 1 такта */ void Filter_SetMode(byte channel, byte mode) { // 1-4 - установка режима работы (используются только 1 и 2 (коды 00 и 01 соответственно)) #ifdef DEBUG_MAX261 Serial.print("FilterSetMode: "); Serial.print(channel); Serial.print(", mode: "); Serial.println(mode); #endif // для режима адрес 0, а старший бит у фильтра B - единица. uint16_t mask = WR_FILTER + ((channel & 1) << 7) + ((mode << 2) & 0x0c); WRITE_TO_FILTER #ifdef DEBUG_MAX261 checkFilter(mask); #endif } void Filter_SetQ(byte channel, byte cQ) { // установка добротности фильтра, 7 разрядов, WR,-,-,-,-,-,-,-,A3,A2,A1,A0,D1,D0,-,- // добротность имеет 7 разрядов и пишется по адресам с 4 по 7, старший бит адреса для порта B равен 1 #ifdef DEBUG_MAX261 Serial.print("FilterSetQ: "); Serial.print(channel); Serial.print(", Q: "); Serial.println(cQ); #endif uint16_t mask = WR_FILTER + ((channel &1) << 7) + ((cQ << 2) & 0x0c) + 0x40; WRITE_TO_FILTER #ifdef DEBUG_MAX261 checkFilter(mask); #endif mask = WR_FILTER + ((channel &1) << 7) + ((cQ ) & 0x0c) + 0x50; WRITE_TO_FILTER #ifdef DEBUG_MAX261 checkFilter(mask); #endif mask = WR_FILTER + ((channel &1) << 7) + ((cQ >> 2) & 0x0c) + 0x60; WRITE_TO_FILTER #ifdef DEBUG_MAX261 checkFilter(mask); #endif mask = WR_FILTER + ((channel &1) << 7) + ((cQ >> 4) & 0x0c) + 0x70; WRITE_TO_FILTER #ifdef DEBUG_MAX261 checkFilter(mask); #endif } void Filter_SetCoeff(byte channel, byte cF) { // установка доп. коэффициента частоты (от 100 до 200) - 6 разрядов от 1 до 63 #ifdef DEBUG_MAX261 Serial.print("FilterSetCoeff: "); Serial.print(channel); Serial.print(", fr: "); Serial.println(cF); #endif uint16_t mask = WR_FILTER + ((channel &1) << 7) + ((cF << 2) & 0x0c) + 0x10; WRITE_TO_FILTER #ifdef DEBUG_MAX261 checkFilter(mask); #endif mask = WR_FILTER + ((channel &1) << 7) + ((cF ) & 0x0c) + 0x20; WRITE_TO_FILTER #ifdef DEBUG_MAX261 checkFilter(mask); #endif mask = WR_FILTER + ((channel &1) << 7) + ((cF >> 2) & 0x0c) + 0x30; WRITE_TO_FILTER #ifdef DEBUG_MAX261 checkFilter(mask); #endif } void checkFilter(uint16_t mask) { static uint16_t currStateA = 0; static uint16_t currStateB = 0; uint16_t addr = (mask & 0x00f0) >> 4; uint16_t data = (mask & 0x000c) >> 2; if(addr & 0x0008) { // channel B currStateB &= ~(0x0003 << ((addr & 0x0007)*2)); currStateB |= data << ((addr & 0x0007)*2); Serial.print("channel B "); Serial.print(currStateB, HEX); Serial.print(", mode : "); Serial.print(currStateB & 3); Serial.print(", freq: "); Serial.print(36000.0/(TIMER2_BASE->ARR+1)/((((currStateB >> 2) & 0x003f) + 64)*1.11072)); Serial.print(", Q: "); Serial.print((currStateB >> 8) & 0x007f); Serial.print(", mask: "); Serial.println(mask, HEX); } else { // channel A currStateA &= ~(0x0003 << ((addr & 0x0007)*2)); currStateA |= data << ((addr & 0x0007)*2); Serial.print("channel A "); Serial.print(currStateA, HEX); Serial.print(", mode : "); Serial.print(currStateA & 3); Serial.print(", freq: "); Serial.print(36000.0/(TIMER2_BASE->ARR+1)/((((currStateA >> 2) & 0x003f) + 64)*1.11072)); Serial.print(", Q: "); Serial.print((currStateA >> 8) & 0x007f); Serial.print(", mask: "); Serial.println(mask, HEX); } }Управление:
- энкодер переключает диапазоны: 5 кГц, 10 кГц, 20 кГц, 40 кГц или 60 кГц,
- кнопка энкодера по кругу переключает: только спектр, спектр + точечная осциллограмма, спектр + осциллограмма линиями,
- тумблер на 3 положения: +0 дБ, +15дБ, +30 дБ,
- плавный регулятор уровня.