Пробуем подключить генератор Si5351. Взял всем известную платку модуля Si5351 от Adafruit и немного модернизировал. Так как питание STM32 осуществляется напряжением +3,3v то на платке модуля нам не нужен преобразователь уровней с +5v до +3,3v на шине i2c. Я, беспощадно, феном «сдул» два подтягивающих резистора номиналом 10k по входу платки модуля линий SCL и SDA и два транзистора BSS138, вместо транзисторов установил перемычки, чтобы использовать родные PLS пины на платке модуля
К «синей таблетке» модуль подключил по следующей схеме
единственное на схеме не указано что пин +5v «синей таблетки» заведен на пин VIN платки модуля Si5351.
Далее изменил конфигурацию пинов STM32 для включения модуля i2c
Портировал код управления Si5351 с AVR в текущий проект, в проекте на AVR у меня создана простая библиотека управления сишкой, ниже листинги файлов si5351a.h и si5351a.с
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | /* * si5351а.h * * Author: R9OFG (ex R0AEK) * Version - 2.2 beta * * info@r9ofg.ru * https://r9ofg.ru */ #ifndef SI5351A_H #define SI5351A_H //#include <inttypes.h> /*установки значений регистров*/ #define SI_CLK0_CONTROL 16 #define SI_CLK1_CONTROL 17 #define SI_CLK2_CONTROL 18 #define SI_SYNTH_PLL_A 26 //#define SI_SYNTH_PLL_B 34 #define SI_SYNTH_MS_0 42 //#define SI_SYNTH_MS_1 50 //#define SI_SYNTH_MS_2 58 #define SI_PLL_RESET 177 /*установки R делителя*/ #define SI_R_DIV_1 0x00 #define SI_R_DIV_2 0x10 #define SI_R_DIV_4 0x20 #define SI_R_DIV_8 0x30 #define SI_R_DIV_16 0x40 #define SI_R_DIV_32 0x50 #define SI_R_DIV_64 0x60 #define SI_R_DIV_128 0x70 #define SI_CLK_SRC_PLL_A 0x00 #define FREQ_PLLA 600000000 #define ACTUAL_MULTIPLIER 1048575 void si5351aOutputOff(); void si5351aSetFrequency(uint32_t frequency); #endif /* SI5351A_H */ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | /* * si5351а.с * * Author: R9OFG (ex R0AEK) * * base on code from Hans Summers, 2015 * Website: http://www.hanssummers.com * A very very simple Si5351a demonstration * using the Si5351a module kit http://www.hanssummers.com/synth * Please also refer to SiLabs AN619 which describes all the registers to use * * Version - 2.2 beta * * info@r9ofg.ru * https://r9ofg.ru */ #include "Main.h" #include "si5351a.h" #define SI5351A_I2C_ADDRESS 0x60 #define I2C_HANDLE hi2c1 extern I2C_HandleTypeDef I2C_HANDLE; uint32_t xtall_FREQ = 25000000; //частота в Гц используемого кварцевого резонатора для тактирвоания Si5351A uint8_t si_DSC; //значение тока драйвера выхода Si5351A - 0/1/2/3 /* записываем 8-битное значение регистра SI5351A по шине I2C */ void si5351_write_data(uint8_t reg, uint8_t value) { while (HAL_I2C_IsDeviceReady(&I2C_HANDLE, (uint16_t)(SI5351A_I2C_ADDRESS<<1), 3, HAL_MAX_DELAY) != HAL_OK) { } HAL_I2C_Mem_Write(&I2C_HANDLE, //i2c указатель на структуру, в которой хранится информация о модуле I2C (uint8_t)(SI5351A_I2C_ADDRESS<<1), //i2c адрес, выравнивание по левому краю (uint8_t)reg, //адрес регистра I2C_MEMADD_SIZE_8BIT, //si5351 используем 8-битные адреса регистров (uint8_t*)(&value), //записываем возвращенные данные в эту переменную 1, //сколько байтов ожидать возврата HAL_MAX_DELAY); //тайм-аут } /* настройки для PLL с mult, num и denom mult в диапазоне 15..90 num в диапазоне 0..1,048,575 (0xFFFFF) denom в диапазоне 0..1,048,575 (0xFFFFF) */ void setupPLL(uint8_t pll, uint8_t mult, uint32_t num, uint32_t denom) { uint32_t P1; // PLL конфигурационный регистр P1 uint32_t P2; // PLL конфигурационный регистр P2 uint32_t P3; // PLL конфигурационный регистр P3 P1 = (uint32_t)(128 * ((float)num / (float)denom)); P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512); P2 = (uint32_t)(128 * ((float)num / (float)denom)); P2 = (uint32_t)(128 * num - denom * P2); P3 = denom; //передаем в регистры si5351a si5351_write_data(pll + 0, (P3 & 0x0000FF00) >> 8); si5351_write_data(pll + 1, (P3 & 0x000000FF)); si5351_write_data(pll + 2, (P1 & 0x00030000) >> 16); si5351_write_data(pll + 3, (P1 & 0x0000FF00) >> 8); si5351_write_data(pll + 4, (P1 & 0x000000FF)); si5351_write_data(pll + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16)); si5351_write_data(pll + 6, (P2 & 0x0000FF00) >> 8); si5351_write_data(pll + 7, (P2 & 0x000000FF)); } /* установка MultiSynth с целочисленным делителем и R-делителем R-делитель - это битовое значение, которое помещается в соответствующий регистр, см. #define в si5351a.h */ void setupMultisynth(uint8_t synth, uint32_t divider, uint8_t rDiv) { uint32_t P1; uint32_t P2; uint32_t P3; P1 = 128 * divider - 512; // P2 = 0, P3 = 1 форсирует целочисленное значение для делителя P2 = 0; P3 = 1; //передаем в регистры si5351a si5351_write_data(synth + 0, (P3 & 0x0000FF00) >> 8); si5351_write_data(synth + 1, (P3 & 0x000000FF)); si5351_write_data(synth + 2, ((P1 & 0x00030000) >> 16) | rDiv); si5351_write_data(synth + 3, (P1 & 0x0000FF00) >> 8); si5351_write_data(synth + 4, (P1 & 0x000000FF)); si5351_write_data(synth + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16)); si5351_write_data(synth + 6, (P2 & 0x0000FF00) >> 8); si5351_write_data(synth + 7, (P2 & 0x000000FF)); } /* в AN619описаны значения значения битов - 0x80 отключает выходной каскад */ void si5351aOutputOff() { //выключаем ВСЕ выходы CLK0, CLK1 и CLK2 si5351_write_data(SI_CLK0_CONTROL, 0x80); si5351_write_data(SI_CLK1_CONTROL, 0x80); si5351_write_data(SI_CLK2_CONTROL, 0x80); } /* установка тока драйвера CLK0 */ void set_drvSt_si5351(int value) { switch (value) { case 0: //сила тока драйвера 2mA si5351_write_data(SI_CLK0_CONTROL, 0x4C | SI_CLK_SRC_PLL_A); break; case 1: //сила тока драйвера 4mA si5351_write_data(SI_CLK0_CONTROL, 0x4D | SI_CLK_SRC_PLL_A); break; case 2: //сила тока драйвера 6mA si5351_write_data(SI_CLK0_CONTROL, 0x4E | SI_CLK_SRC_PLL_A); break; case 3: //сила тока драйвера 8mA si5351_write_data(SI_CLK0_CONTROL, 0x4F | SI_CLK_SRC_PLL_A); break; } } /* включаем выход CLK0 и генерируем указанную частоту в этом примере используется PLLA задаваемая частота должна находится в диапазоне от 1 МГц до 150 МГц пример использования: si5351aSetFrequency(10000000); включит выход CLK0 с генерируемой частотой 10 МГц */ void si5351aSetFrequency(uint32_t frequency) { uint32_t pllFreq; uint32_t l; float f; uint8_t mult = 0; uint32_t num = 0; uint32_t denom; uint32_t divider; /*расчет коэффициента деления. 900,000,000 Hz - максимальная частота для PLLA*/ divider = FREQ_PLLA / frequency; /*целочисленное деление*/ if (divider % 2) divider--; /*расчет частоты pllFreq: делитель * желаемую выходную частоту*/ pllFreq = divider * frequency; /*расчет множителя для получения заданной pllFreq состоит из трех частей: 1: mult - целое число, которое должно быть в диапазоне 15..90 2: num и denom - дробные части, числитель и знаменатель каждый из них 20 bits (диапазон 0..1048575) 3: фактический множитель mult + num / denom для простоты мы установили знаменатель на максимум 1048575 (ACTUAL_MULTIPLIER)*/ mult = pllFreq / xtall_FREQ; l = pllFreq % xtall_FREQ; f = l; f *= ACTUAL_MULTIPLIER; f /= xtall_FREQ; num = f; denom = ACTUAL_MULTIPLIER; /*настройка PLLA с рассчитанным коэффициентом умножения*/ setupPLL(SI_SYNTH_PLL_A, mult, num, denom); /*установка делителя MultiSynth 0 с рассчитанным делителем последний этап деления R может делиться на степень 2, начиная с 1..128. представлен константами SI_R_DIV1....SI_R_DIV128 (см. заголовочный файл si5351a.h) если вы хотите вывести частоты ниже 1 МГц, вы должны использовать SI_R_DIV128*/ setupMultisynth(SI_SYNTH_MS_0, divider, SI_R_DIV_1); /*сброс PLL, это вызывает щелчки при перестройке, для незначительных изменений в параметрах, НЕ ИСПОЛЬЗУЕМ сброс PLL, рекомендуется использовать только при значительном изменении частоты, например смена диапазона*/ //i2cSendRegister(SI_PLL_RESET, 0xA0); /*включение выхода CLK0, параметр - выбор силы тока драйвера и установка для входа MultiSynth0 значения PLLA*/ set_drvSt_si5351(si_DSC); } |
Изменил файл main.c, добавил указатели на переменные из библиотеки управления сишкой
и собственно вызов функции настройки сишки
т.е. сначала отдаем команду на выключение всех выходов сишки, затем выбираем значение тока драйвера выхода сишки и в завершении отдаем команду на включение выхода CLK0 с генерацией частоты равной 15МГц. Далее менять частоту на выходе сишки можно вызывая одну функцию
si5351aSetFrequency(частота в Гц);
Вот как стенд выглядит
и результат измерений осой
оса показывает частоту на выходе сишки равную 15,00542 МГц, дело в том, что используемые кварцевые резонаторы на платках модуля Si5351 не отличаются точностью номинальной частоты, отсюда и ошибка в калькуляции для установки регистров Si5351, лечится это просто — указывается реальная частота кварцевого резонатора, в проекте синтезатора я использовал следующий метод коррекции выходной частоты — в режиме настройки синтезатора валкодером, на выходе, устанавливается частота по нулям со сравниваемой и записывается новое значение частоты кварцевого резонатора в EEPROM, таким образом при включении синтезатора считывается откорректированное значение частоты кварцевого резонатора и расчет идет уже с верными входными данными, в результате уход частоты синтезатора в диапазоне 1…30МГц составляет не более +/-5 Гц на краях всего диапазона!
Продолжение следует…
73!