Перейти к содержанию
    

Ручной выбор VCO в MAX2871

Суть проблемы: нужно быстро менять частоту в MAX2871. Поэтому был сделан ручной выбор VCO. Подобрали массив со значением VCO. На первых двух партиях все было хорошо. Затем столкнулись с тем, что на партии в 50 штук примерно 1/4 не заработала - приходиться менять VCO (те подбирать под каждую плату), что есть очень трудоемкий процесс.

Решил сделать как советует даташит - прогнать по всем частотам в режиме автовыбора, запомнить номера VCO, а затем использовать номера в режиме ручного выбора. (стр. 27 VCO Manual Selection Operation)

А вот не работает! Читаю, записываю, а генерация на другой частоте, подбираю VCO - все в порядке. В данный момент - делаю только для одной частоты, а не для всей сетки частот.

Вот собственное код...

volatile uint32_t R3=0;							// Одиносное содержимое R3
volatile uint32_t R6=0;							// Одиносное содержимое R3

uint8_t RevByte(uint8_t a)						// Обратный порядок битов в байте
{
    a=((a>>1) & 0x55) | ((a<<1) & 0xAA);
    a=((a>>2) & 0x33) | ((a<<2) & 0xCC);   
    a=((a>>4) & 0x0F) | ((a<<4) & 0xF0); 
    return a;
}

void SPI_WriteDWord(uint32_t a)                 // Функция записи двойного слова в SPI
{
    uint8_t b=0;                                // Временная переменная для байта
    LE_LAT=0;                                   // Запись байта в SPI
    b=a>>24;                                    // Выбор четвертого (старшего) байта
    SPI1_ExchangeByte(b);                       // Запись байта в SPI
    b=a>>16;                                    // Выбор третьего байта
    SPI1_ExchangeByte(b);                       // Запись байта в SPI
    b=a>>8;                                     // Выбор второго байта
    SPI1_ExchangeByte(b);                       // Запись байта в SPI
    b=a;                                        // Выбор первого (младшего) байта
    SPI1_ExchangeByte(b);                       // Запись байта в SPI 
    LE_LAT=1;
}

// Загрузка всех настроек в MAX2871
void SetMAX2871(uint32_t R0, uint32_t R1, uint32_t R2, uint32_t R3, uint32_t R4, uint32_t R5)
{
    SPI_WriteDWord(R5);
    SPI_WriteDWord(R4);                       // Запись регистра
    SPI_WriteDWord(R3);
    SPI_WriteDWord(R2);
    SPI_WriteDWord(R1);
    SPI_WriteDWord(R0);
}

uint32_t SPI_ReadDWord()                 		// Функция чтения
{
    uint32_t spi_data=0;						// Считанное значение
    uint8_t spi_packet[4];						// Байтовый массив
    uint8_t i=0;
    SPI_WriteDWord(0x00000006);					// Запись с адресом R6
    for ( i = 0; i < 4; i++)   					
    {
       spi_packet[i] = SPI1_ExchangeByte(0x00); // Чтение
    }
    for ( i = 0; i < 4; i++)
    {
        spi_data |= RevByte(spi_packet[i]);  	// Формируем резусльтат с изменнеием порядка битов
        //spi_data |= spi_packet[i];
        if ( i != 3) spi_data = spi_data << 8;
    }
     return spi_data;
}

void CalibrateVCO()								// Калибровка на нужные VCO
{
    uint8_t i=0;
    uint32_t a=0;
    SPI_WriteDWord(0x00001F23);					// Пишем R3 с атовыбором VCO
    SPI_WriteDWord(0x005C0110);					// Пишем R0
    while (LD_PORT==0) {};						// Ждем захвата
    R6=SPI_ReadDWord();							// Читаем R6
    a=R6;
    a=a<<23;									// Формируем R3 на основании VCO из R6
    a=a & 0xFC000000;
    a=a | 0x02001F23;   
    R3=a;   
}

void main(void)
{
    SYSTEM_Initialize();
    uint16_t i2g=0;
    uint32_t a=0;
    uint8_t b=0;                                // Временная переменная для байта
    uint8_t i=0;  
    // Enable the Global Interrupts
    //INTERRUPT_GlobalInterruptEnable();
    // Disable the Global Interrupts
    //INTERRUPT_GlobalInterruptDisable();
    // Enable the Peripheral Interrupts
    //INTERRUPT_PeripheralInterruptEnable();
    // Disable the Peripheral Interrupts
    //INTERRUPT_PeripheralInterruptDisable();
    SPI1_Open(SPI1_DEFAULT);                      // Открытие SPI
	// Инициализация MAX2871
    RFPDN_LAT=0;
    CE_LAT=1;
    LE_LAT=1;
    SetMAX2871(0x07D00000,0x400103E9,0x11035F42,0x00001F23,0x60A140FC,0x00440005);
    __delay_ms(25);
    SetMAX2871(0x07D00000,0x400103E9,0x11035F42,0x00001F23,0x60A140FC,0x00440005);
    __delay_ms(20);
    SetMAX2871(0x005C0110,0x200101A1,0x10005F42,0x00001F23,0x629080FC,0x00440005);
    RFPDN_LAT=1;
	// Конец инициализации MAX2871
    __delay_ms(2000);
    CalibrateVCO();
    __delay_ms(1000);
    
    while (1)
    {  
            LED2_LAT=~LED2_LAT;
			SPI_WriteDWord(R3);							// Пишем анее заготовленное R3
            SPI_WriteDWord(0x005C0110);					// Пишем R0
            while (LD_PORT==0) {};						// Ждем захвата
            i2g++;                          
            if (i2g==83) {i2g=0;}                       // Сброс счетчика            
            __delay_ms(1000);
    }
}

В чем может быть проблема? Может что то не так с чтением? Хотя ID микросхемы и адрес прочитанного регистра правильные.

Читается так
01111000 00000000 00000000 11011110‬ [0x780000DE] R6 Читаю
01101110 00000000 00011111 00100011 [0x6E001F23] R3 Формирую и пишу - Не работает
 
10100010 00000000 00011111 00100011‬ [0xA2001F23] R3 Подгоняю - а так работает
 
Может какие то особенности в настройках МС не учел? Куда копать?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Пытаетесь использовать VCO без PLL?
У VCO всегда будет какой-то разброс частоты.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У MAX2871 есть полезная фича- встроенный АЦП для считывания Vtune. Если после захвата напряжения управления VCO близки к верхнему или нижнему пределу то скорее всего захват сорвет и и автомат выберет соседний поддиапазон VCO. А еще есть хитрый бит VAS_temp. При его использовании выбор поддиапазона VCO более надежен, но время настройки вообще под 100 ms и более.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

PLL включен, только работает он на одном заранее определенном VCO, смысл в том, чтобы не тратить время на его автоматический выбор.

Не понимаю, как знание напряжения Vtune мне поможет? Насколько я представляю, если мы узнали какой VCO был выбран в автоматическом режиме - мы можем его выбрать и в ручном, при тех же настройках и для той же частоты... При выполнении калибровки Vtune примерно 1,3В, а при попытки его задать вручную, значение стремиться к верхнему пределу примерно 3,27. При его подборе вручную - все работает, напряжение около 1,3В, вот только номер VCO там совсем другой выходит. Так по автовыбор выходит VCO 27, а работает по подбору на VCO 40.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поставьте буфер из ОУ на v-tune и посмотрите осциллографом на процесс самокалибровки и на то как ведет себя сигнал LD. В общем виде предполагаю что читаете номер VCO слишком рано, когда процесс калибровки еще не завершился.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Только что, khach сказал:

Поставьте буфер из ОУ на v-tune и посмотрите осциллографом на процесс самокалибровки и на то как ведет себя сигнал LD. В общем виде предполагаю что читаете номер VCO слишком рано, когда процесс калибровки еще не завершился.

пробовал ставить задержки разные от 1мкс до 200мс после ожидания LD - эффекта никакого, читается то же значение.

Склоняюсь к мысли, что все-таки что то не так с чтением R6...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Странно что на столько номера VCO отличаются- обычно это отличие на 1-2 номера макс. Вот небольшой пример, но там никаких особых изысков не видно.

https://nuclearrambo.com/wordpress/manual-vco-selection-for-a-faster-locking-pll/

Даташит надеюсь полный на MAX  3-4 версии, где синхронизация нескольких MAX по фазе описана и процедура выбора VCO. Ид микросхемы совпадает, чтобы точно быть уверенным что не перемаркировка?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 минуты назад, khach сказал:

Странно что на столько номера VCO отличаются- обычно это отличие на 1-2 номера макс. Вот небольшой пример, но там никаких особых изысков не видно.

https://nuclearrambo.com/wordpress/manual-vco-selection-for-a-faster-locking-pll/

Даташит надеюсь полный на MAX  3-4 версии, где синхронизация нескольких MAX по фазе описана и процедура выбора VCO. Ид микросхемы совпадает, чтобы точно быть уверенным что не перемаркировка?

Даташит 3-й версии, там как раз добавлен ручной выбор VCO. ID с совпадает. Да и в остальном все работает штатно. Сегодня попробую вечером подключить сниффер на SPI  - посмотреть, что там реально. Несколько смущает, необходимость реверса бит при чтении - по идее такого не должно требоваться.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Исходники посмотрите https://os.mbed.com/users/MI/code/max2871/

uint32_t MAX2871::readRegister6()
{
    uint32_t raw, reg6read;
    
    reg5.bits.mux = 1;
    reg2.bits.mux = 0x4;
    write(reg5.all);
    write(reg2.all);
    
    write(0x00000006);
    
    m_spiBus.format(8,1);
    
    raw = m_spiBus.write(0x00);
    reg6read = (reg6read & 0x01FFFFF) + (raw << 25);
    raw = m_spiBus.write(0x00);
    reg6read = (reg6read & 0xFE01FFFF) + (raw << 17); 
    raw = m_spiBus.write(0x00);
    reg6read = (reg6read & 0xFFFE01FF) + (raw << 9);
    raw = m_spiBus.write(0x00);
    reg6read = (reg6read & 0xFFFFFE01) + (raw << 1);
    m_spiBus.write(0x00);
    
    m_spiBus.format(8,0);
    
    return reg6read;
}

Вроде никакого реверса нет. Только есть хлолостая передача байта после чтения регистра, незнаю зачем.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 минут назад, khach сказал:

Исходники посмотрите https://os.mbed.com/users/MI/code/max2871/

uint32_t MAX2871::readRegister6()
{
    uint32_t raw, reg6read;
    
    reg5.bits.mux = 1;
    reg2.bits.mux = 0x4;
    write(reg5.all);
    write(reg2.all);
    
    write(0x00000006);
    
    m_spiBus.format(8,1);
    
    raw = m_spiBus.write(0x00);
    reg6read = (reg6read & 0x01FFFFF) + (raw << 25);
    raw = m_spiBus.write(0x00);
    reg6read = (reg6read & 0xFE01FFFF) + (raw << 17); 
    raw = m_spiBus.write(0x00);
    reg6read = (reg6read & 0xFFFE01FF) + (raw << 9);
    raw = m_spiBus.write(0x00);
    reg6read = (reg6read & 0xFFFFFE01) + (raw << 1);
    m_spiBus.write(0x00);
    
    m_spiBus.format(8,0);
    
    return reg6read;
}

Вроде никакого реверса нет. Только есть хлолостая передача байта после чтения регистра, незнаю зачем.

Спасибо! Вечером попробую, может мне этой холостой передачи не хватает... Завтра отпишусь по результату.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Продолжил изыскания... Все-таки, что то не так с чтением. Пробовал разные вариации на тему чтения по примеру разных исходных кодов найденных в сети - результата нет. Зато обнаружил, что при изменении скорости SPI читаются разные данные (при 1МГц и 4МГц - одно значение, а при 16МГц - другое). А вот запись во всех случаях идет корректно и МС встает на нужную частоту. Что еще странно - после окончания калибровки и просто записи - на выходе SPI от MAX2871 продолжают идти данные, хотя команды на чтения не давал. В общем буду искать лог. анализатор, без него никак.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В итоге - победил. Причина действительно оказалась в реализации чтения. А конкретно в этой фразе из dataseet: "Finally, send 1 period of the clock." В общем, нужно дать один дополнительный тактовый импульс на CLK пред чтением данных. Поскольку SPI аппаратный - отключаю вывод от модуля SPI, передергиваю его и возвращаю на место. К тому же нужно сдвинуть полученные данные на два бита влево. Все заработало как нужно. Сначала выясняю какой VCO выбран в автомате для каждой частоты, а затем шишу их с ручным выбором.

Рабочий код - вдруг кому то пригодиться...

uint32_t SPI_ReadDWord()                 		// Функция чтения
{
    uint32_t spi_data=0;						// Считанное значение
    uint8_t spi_packet[4]={0};					// Байтовый массив
    uint8_t i=0;
    LE_LAT=0;                                   // Запись байта в SPI
    SPI1_ExchangeByte(0x00);                    // Запись байта в SPI
    SPI1_ExchangeByte(0x00);                    // Запись байта в SPI
    SPI1_ExchangeByte(0x00);                    // Запись байта в SPI
    SPI1_ExchangeByte(0x06);                    // Запись байта в SPI 
    LE_LAT=1;									// Запись с адресом R6   
    RB2PPS = 0x00;   							//RB2->LAT; 
    RB2_SetHigh();								// Один тактовый импульс
    RB2_SetLow();
    RB2PPS = 0x0D;   							//RB2->MSSP1:SCK1; 
    for ( i = 0; i < 4; i++)   					
    {
       spi_packet[i] = SPI1_ExchangeByte(0x00); // Чтение
    }
    for ( i = 0; i < 4; i++)
    {
        spi_data |= spi_packet[i];
        if ( i != 3) spi_data = spi_data << 8;
    }
    spi_data=spi_data<<2;						// Сдвиг на два бита
    return spi_data;
}

 

Изменено пользователем ppram5

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если массив частотных точек великоват, то удобней составить таблицу из граничных частот диапазонов каждого гун. Для этого mux_out перепрограммируется на выдачу сигнала с выхода делителя частоты N, делённого пополам (MUX = 0100) . Для каждого принудительно выставленного номера гун устанавливается поочерёдно charge pump в source mode и sink mode (CPT = 10 и 01 соответственно), при этом считываются частоты следования импульсов на выходе MUX и делается пересчёт в граничные частоты гун.

Следует не забывать, что границы частот немного плывут с изменением температуры. Так что лучше, например, формировать три таблицы: при - 40, +25 и +85 градусах, и интерполировать результат. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, AFK сказал:

Если массив частотных точек великоват, то удобней составить таблицу из граничных частот диапазонов каждого гун. Для этого mux_out перепрограммируется на выдачу сигнала с выхода делителя частоты N, делённого пополам (MUX = 0100) . Для каждого принудительно выставленного номера гун устанавливается поочерёдно charge pump в source mode и sink mode (CPT = 10 и 01 соответственно), при этом считываются частоты следования импульсов на выходе MUX и делается пересчёт в граничные частоты гун.

Следует не забывать, что границы частот немного плывут с изменением температуры. Так что лучше, например, формировать три таблицы: при - 40, +25 и +85 градусах, и интерполировать результат. 

Количество частот в общем случае не более 100 или 200.

Именно чтобы не зависеть от разброса каждого экземляра МС и от внешней температуры (уличная эксплуатация) я и возился с таким методом. К тому же планирую добавить таймер, проверяющий наличие генерации - если сорвалась - перекалибровка.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 часов назад, ppram5 сказал:

В итоге - победил. Причина действительно оказалась в реализации чтения. А конкретно в этой фразе из dataseet: "Finally, send 1 period of the clock." В общем, нужно дать один дополнительный тактовый импульс на CLK пред чтением данных. Поскольку SPI аппаратный - отключаю вывод от модуля SPI, передергиваю его и возвращаю на место.

Долго переключать. Проще послать еще один байт или слово с нулями чтобы адрес не выбрался и передернуть LE чтобы автомат SPI обнулился. А считаное в первом обмене тогда будет сдвинуто на один бит по сравнению с даташитом. Кстати, поэтому и сдвиги на 25 17 9 и 1 в коде индуса.

6 часов назад, AFK сказал:

Для каждого принудительно выставленного номера гун устанавливается поочерёдно charge pump в source mode и sink mode (CPT = 10 и 01 соответственно), при этом считываются частоты следования импульсов на выходе MUX и делается пересчёт в граничные частоты гун.

Спасибо, хороший метод для 2870 где нет АЦП

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...