ppram5 0 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба Суть проблемы: нужно быстро менять частоту в 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 Подгоняю - а так работает Может какие то особенности в настройках МС не учел? Куда копать? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Jurenja 1 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба Пытаетесь использовать VCO без PLL? У VCO всегда будет какой-то разброс частоты. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
khach 43 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба У MAX2871 есть полезная фича- встроенный АЦП для считывания Vtune. Если после захвата напряжения управления VCO близки к верхнему или нижнему пределу то скорее всего захват сорвет и и автомат выберет соседний поддиапазон VCO. А еще есть хитрый бит VAS_temp. При его использовании выбор поддиапазона VCO более надежен, но время настройки вообще под 100 ms и более. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ppram5 0 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба PLL включен, только работает он на одном заранее определенном VCO, смысл в том, чтобы не тратить время на его автоматический выбор. Не понимаю, как знание напряжения Vtune мне поможет? Насколько я представляю, если мы узнали какой VCO был выбран в автоматическом режиме - мы можем его выбрать и в ручном, при тех же настройках и для той же частоты... При выполнении калибровки Vtune примерно 1,3В, а при попытки его задать вручную, значение стремиться к верхнему пределу примерно 3,27. При его подборе вручную - все работает, напряжение около 1,3В, вот только номер VCO там совсем другой выходит. Так по автовыбор выходит VCO 27, а работает по подбору на VCO 40. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
khach 43 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба Поставьте буфер из ОУ на v-tune и посмотрите осциллографом на процесс самокалибровки и на то как ведет себя сигнал LD. В общем виде предполагаю что читаете номер VCO слишком рано, когда процесс калибровки еще не завершился. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ppram5 0 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба Только что, khach сказал: Поставьте буфер из ОУ на v-tune и посмотрите осциллографом на процесс самокалибровки и на то как ведет себя сигнал LD. В общем виде предполагаю что читаете номер VCO слишком рано, когда процесс калибровки еще не завершился. пробовал ставить задержки разные от 1мкс до 200мс после ожидания LD - эффекта никакого, читается то же значение. Склоняюсь к мысли, что все-таки что то не так с чтением R6... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
khach 43 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба Странно что на столько номера VCO отличаются- обычно это отличие на 1-2 номера макс. Вот небольшой пример, но там никаких особых изысков не видно. https://nuclearrambo.com/wordpress/manual-vco-selection-for-a-faster-locking-pll/ Даташит надеюсь полный на MAX 3-4 версии, где синхронизация нескольких MAX по фазе описана и процедура выбора VCO. Ид микросхемы совпадает, чтобы точно быть уверенным что не перемаркировка? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ppram5 0 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба 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 - посмотреть, что там реально. Несколько смущает, необходимость реверса бит при чтении - по идее такого не должно требоваться. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
khach 43 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба Исходники посмотрите 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; } Вроде никакого реверса нет. Только есть хлолостая передача байта после чтения регистра, незнаю зачем. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ppram5 0 26 октября, 2022 Опубликовано 26 октября, 2022 · Жалоба 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; } Вроде никакого реверса нет. Только есть хлолостая передача байта после чтения регистра, незнаю зачем. Спасибо! Вечером попробую, может мне этой холостой передачи не хватает... Завтра отпишусь по результату. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ppram5 0 27 октября, 2022 Опубликовано 27 октября, 2022 · Жалоба Продолжил изыскания... Все-таки, что то не так с чтением. Пробовал разные вариации на тему чтения по примеру разных исходных кодов найденных в сети - результата нет. Зато обнаружил, что при изменении скорости SPI читаются разные данные (при 1МГц и 4МГц - одно значение, а при 16МГц - другое). А вот запись во всех случаях идет корректно и МС встает на нужную частоту. Что еще странно - после окончания калибровки и просто записи - на выходе SPI от MAX2871 продолжают идти данные, хотя команды на чтения не давал. В общем буду искать лог. анализатор, без него никак. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ppram5 0 31 октября, 2022 Опубликовано 31 октября, 2022 (изменено) · Жалоба В итоге - победил. Причина действительно оказалась в реализации чтения. А конкретно в этой фразе из 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; } Изменено 31 октября, 2022 пользователем ppram5 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AFK 0 31 октября, 2022 Опубликовано 31 октября, 2022 · Жалоба Если массив частотных точек великоват, то удобней составить таблицу из граничных частот диапазонов каждого гун. Для этого mux_out перепрограммируется на выдачу сигнала с выхода делителя частоты N, делённого пополам (MUX = 0100) . Для каждого принудительно выставленного номера гун устанавливается поочерёдно charge pump в source mode и sink mode (CPT = 10 и 01 соответственно), при этом считываются частоты следования импульсов на выходе MUX и делается пересчёт в граничные частоты гун. Следует не забывать, что границы частот немного плывут с изменением температуры. Так что лучше, например, формировать три таблицы: при - 40, +25 и +85 градусах, и интерполировать результат. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ppram5 0 31 октября, 2022 Опубликовано 31 октября, 2022 · Жалоба 1 минуту назад, AFK сказал: Если массив частотных точек великоват, то удобней составить таблицу из граничных частот диапазонов каждого гун. Для этого mux_out перепрограммируется на выдачу сигнала с выхода делителя частоты N, делённого пополам (MUX = 0100) . Для каждого принудительно выставленного номера гун устанавливается поочерёдно charge pump в source mode и sink mode (CPT = 10 и 01 соответственно), при этом считываются частоты следования импульсов на выходе MUX и делается пересчёт в граничные частоты гун. Следует не забывать, что границы частот немного плывут с изменением температуры. Так что лучше, например, формировать три таблицы: при - 40, +25 и +85 градусах, и интерполировать результат. Количество частот в общем случае не более 100 или 200. Именно чтобы не зависеть от разброса каждого экземляра МС и от внешней температуры (уличная эксплуатация) я и возился с таким методом. К тому же планирую добавить таймер, проверяющий наличие генерации - если сорвалась - перекалибровка. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
khach 43 31 октября, 2022 Опубликовано 31 октября, 2022 · Жалоба 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 где нет АЦП Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться