AN428: программирование синтезаторов Si53xx |
![]() |
Добавил(а) microsin |
В этой документации (перевод [1]) приведен пример программирования синтезатора Si53xx. Это подойдет для синтезаторов на основе DSPLL™ (Any-Frequency Precision Clocks, например Si5326 и Si5368), а также на основе технологии MultiSynth Any-Frequency, генераторы Any-Output Clock (например Si5338, Si5351, Si5356). Применен стартер-кит JumpStart компании Silicon Labs, однако исходный код может быть портирован и на другие платформы. [2. Использование JumpStart] 1. Используйте оценочную плату разработчика и ПО конфигурирования, чтобы создать конфигурацию и соответствующий дамп регистров (register map) программируемой микросхемы синтезатора. 2. Используя инструментарий Silicon Labs MCU Integrated Development Environment (IDE), выполните компиляцию созданной register map вместе MCU firmware, написанном на языке C, и загрузите полученный двоичный код firmware в память микроконтроллера (MCU). Примечание: C2 это двухпроводный протокол обмена Silicon Labs для внутрисхемного программирования и отладки процессоров Silicon Labs. Вместе с китами разработчика MCU поставляется соответствующий адаптер USB с интерфейсом C2. Рис. 2. Шаг 2: передача Register Map в MCU. 3. После сброса или включения питания MCU запишет данные register map в память целевого устройства (синтезатор Si53xx), после этого оно станет полностью сконфигурированным и начнет генерировать частоты. Для MCU используется свой собственный отдельный генератор, поэтому MCU может работать независимо от тактовых частот, которые генерирует целевое конфигурируемое устройство. Рис. 3. Шаг 3: программирование устройства данными Register Map. 4. Как только получена нужная конфигурация firmware, для процесса производства могут использоваться различные опции программирования MCU. Для получения подробной информации свяжитесь с Silicon Labs, или ознакомьтесь с документом [2], где описаны разные опции, включая методы внутрисхемного программирования (ISP) через интерфейсы C2 или JTAG, и как получить заранее запрограммированные MCU. В целях упрощения отслеживания продукции настроенные на заводе пользовательской прошивкой MCU получают уникальные обозначаемые part number. [3. Выбор MCU] Для использования JumpStart рассмотрите следующие функции MCU: • Физический размер корпуса. Есть возможность перейти на однократно программируемые MCU (one-time programmable, OTP) после того, как код будет полностью разработан и отлажен на перешиваемых MCU (с памятью FLASH). OTP MCU значительно дешевле. Используйте онлайн-поиск по параметрам [3] для выбора нужного MCU. [4. Пример использования на основе C8051F30] Silicon Labs C8051F301 (F301) MCU (с размером корпуса 3x3 мм) и микросхема Any-Frequency, Any-Output Clock Generator (Si5338N), подключенная через I2C, в этом примере конфигурируется для приложения gigabit Ethernet (GigE). Частотный план требует входной частоты кварца 25 МГц и двух частот 25 МГц с уровнями 3.3V CMOS на выходе CLK0 и 125 МГц уровней стандарта 3.3V LVDS на CLK1. Рис. 4. Блок схема примера. 4.1. Описание firmware Полный листинг кода см. в Приложении (врезка в конце статьи), и его также можно скачать с сайта. Чтобы найти исходный код примера, введите в строке поиска "AN428", и загрузите AN428 Jumpstart Software, выбрав его в результатах поиска. 1. Даташит Si5338 [4] содержит дополнительную информацию по записи конфигурации пользователя в RAM устройства Si5338 (см. секцию 3.5.3 даташита). 4.2. Кодирование Register Map В этом примере используется кит Si5338-EVB и утилита ClockBuilder Desktop для разработки конфигурации плана частот и сохранения его в текстовый файл register map. В результате получается файл с расширением *.txt, где каждая строка имеет формат: address,datah Примечание: более новая версия ClockBuilder Pro 2.23 (релиз 10 апреля 2018 г.) выдает более подробный текстовый файл следующего формата: Setting Name Location Start Address Start Bit Num Bits NVM Type -------------- ------------ ------------- --------- -------- --- ---- SYS_INIT 0x0000[7] 0x0000 7 1 No R/O LOL_B 0x0000[6] 0x0000 6 1 No R/O LOL_A 0x0000[5] 0x0000 5 1 No R/O CLK_LOS 0x0000[4] 0x0000 4 1 No R/O XO_LOS 0x0000[3] 0x0000 3 1 No R/O REVID 0x0000[1:0] 0x0000 0 2 No R/O SYS_INIT_STKY 0x0001[7] 0x0001 7 1 No R/W Столбец "Setting Name" показывает мнемонику бита или группы бит настроек, "Location" показывает адрес регистра и битовые позиции настройки, что дублируется в столбцах "Start Address", "Start Bit", "Num Bits". Столбец "NVM" показывает, сохраняются ли эти настройки в энергонезависимой памяти (No означает, что настройки хранятся в RAM, Yes в NNM, т. е. Non-Volatile Memory). "Type" означает тип доступа (R/O только для чтения, R/W чтение и запись). Массив register map в исходном коде MCU firmware будет выглядеть следующим образом: {address,0xdata,0xmask}, Поле address представляет адрес регистра в памяти конфигурируемого устройства в десятичном формате. Поле data содержит значение, отправляемое в регистр по соответствующему адресу. Поле mask определяет, какие биты в данных регистра должны игнорироваться. Любой бит, в маске которого стоит лог. 1, используется и подлежит модификации, и любой бит, соответствующий лог. 0 в маске, должен быть сохранен, как он был ранее в регистре. Поля data и mask указываются в шестнадцатеричном формате. Все 3 поля это 8-битные целые числа в диапазоне 0 .. 255. После формирования файлов откройте register_map.h в проекте JumpStart, и вставьте данные register map в декларацию массива Reg_Store между его фигурными скобками. Обновите определение константы NUM_REGS_MAX, чтобы она соответствовала реальному количеству байт в массиве. Обратите внимание, что не все регистры должны быть записаны в Si5338, в зависимости от её используемых функций. Для получения подробной информации по регистрам и их битовым настройкам см. даташит Si5338. 4.3. Функция генерации файла заголовка Начиная с версии 2.5 ClockBuilder Desktop появилась функция "Header File Generation", она позволяет автоматически сохранить информацию register map в формате заголовочного файла на языке C. Этот файл можно использовать в проекте firmware микроконтроллера для программирования регистров используемой микросхемы Si53xx. Чтобы создать этот файл, кликните Options -> Save C Code Header File. Примечание: в версии ClockBuilder Pro 2.23 сохранение заголовочного файла осуществляется по-другому. После открытия проекта выберите Export, после чего в окне диалога настройки на закладке Register File выберите Options Export Type: C Code Header File и нажмите кнопку "Save to File...". Рис. 5. Сохранение заголовочного файла на языке C. После того, как файл сгенерирован, он должен быть скомпилирован вместе с последней версией JumpStart firmware для использования в MCU, для чего файл register_map.h заменяется на новый. Сгенерированный файл заголовка имеет корректный синтаксис C, и содержит следующую информацию: • Всю необходимую информацию настройки выходного драйвера, подсистем Multisynth, конфигурацию входов, расширения выходного спектра, а также настройки частоты и фазы формируемых выходных тактов. Имейте в виду, что сгенерированный заголовочный файл не оптимизирован по содержимому в с сравнении с данными настроек, которые могли бы быть созданы вручную, когда для настройки используется только ограниченное подмножество регистров. Например, если нет необходимости менять настройки расширения спектра и фазы, автоматически сгенерированный файл будет занимать больше места в памяти программ, чем необходимо в действительности. 4.4. Заголовочный файл с данными Register Map // Register map для использования вместе с апноутом AN428 (JumpStart) // http://www.silabs.com/products/clocksoscillators/pages/default.aspx // Copyright 2010 Silicon Laboratories // Сгенерировано ClockBuilder Desktop software version 2.7 // Дата создания: среда, 15 сентября 2010 года, 9:56 AM // Входная частота синтезатора Si5338 (МГц): 25.000000000 #define NUM_REGS_MAX 350 typedef struct Reg_Data { unsigned char Reg_Addr; unsigned char Reg_Val; unsigned char Reg_Mask; } Reg_Data; Reg_Data const code Reg_Store[NUM_REGS_MAX] = { { 0,0x00,0x00}, { 1,0x00,0x00}, { 2,0x00,0x00}, { 3,0x00,0x00}, { 4,0x00,0x00}, { 5,0x00,0x00}, { 6,0x08,0x1D}, { 7,0x00,0x00}, { 8,0x70,0x00}, { 9,0x0F,0x00}, { 10,0x00,0x00}, { 11,0x00,0x00}, { 12,0x00,0x00}, { 13,0x00,0x00}, { 14,0x00,0x00}, { 15,0x00,0x00}, { 16,0x00,0x00}, { 17,0x00,0x00}, { 18,0x00,0x00}, { 19,0x00,0x00}, { 20,0x00,0x00}, { 21,0x00,0x00}, { 22,0x00,0x00}, { 23,0x00,0x00}, { 24,0x00,0x00}, { 25,0x00,0x00}, { 26,0x00,0x00}, { 27,0x70,0x80}, { 28,0x16,0xFF}, { 29,0x90,0xFF}, { 30,0xA0,0xFF}, { 31,0xC0,0xFF}, { 32,0xC0,0xFF}, { 33,0xE3,0xFF}, { 34,0xE3,0xFF}, { 35,0x00,0xFF}, { 36,0x06,0x1F}, { 37,0x03,0x1F}, { 38,0x06,0x1F}, { 39,0x06,0x1F}, { 40,0xE3,0xFF}, { 41,0x0E,0x7F}, { 42,0x23,0x3F}, { 43,0x00,0x00}, { 44,0x00,0x00}, { 45,0x00,0xFF}, { 46,0x00,0xFF}, { 47,0x14,0x3F}, { 48,0x3A,0xFF}, { 49,0x00,0xFF}, { 50,0xC4,0xFF}, { 51,0x07,0xFF}, { 52,0x10,0x7F}, { 53,0x00,0xFF}, { 54,0x08,0xFF}, { 55,0x00,0xFF}, { 56,0x00,0xFF}, { 57,0x00,0xFF}, { 58,0x00,0xFF}, { 59,0x01,0xFF}, { 60,0x00,0xFF}, { 61,0x00,0xFF}, { 62,0x00,0x3F}, { 63,0x10,0x7F}, { 64,0x00,0xFF}, { 65,0x30,0xFF}, { 66,0x00,0xFF}, { 67,0x00,0xFF}, { 68,0x00,0xFF}, { 69,0x00,0xFF}, { 70,0x01,0xFF}, { 71,0x00,0xFF}, { 72,0x00,0xFF}, { 73,0x00,0x3F}, { 74,0x10,0x7F}, { 75,0x00,0xFF}, { 76,0x00,0xFF}, { 77,0x00,0xFF}, { 78,0x00,0xFF}, { 79,0x00,0xFF}, { 80,0x00,0xFF}, { 81,0x00,0xFF}, { 82,0x00,0xFF}, { 83,0x00,0xFF}, { 84,0x00,0x3F}, { 85,0x10,0x7F}, { 86,0x00,0xFF}, { 87,0x00,0xFF}, { 88,0x00,0xFF}, { 89,0x00,0xFF}, { 90,0x00,0xFF}, { 91,0x00,0xFF}, { 92,0x00,0xFF}, { 93,0x00,0xFF}, { 94,0x00,0xFF}, { 95,0x00,0x3F}, { 96,0x10,0x00}, { 97,0x00,0xFF}, { 98,0x30,0xFF}, { 99,0x00,0xFF}, {100,0x00,0xFF}, {101,0x00,0xFF}, {102,0x00,0xFF}, {103,0x01,0xFF}, {104,0x00,0xFF}, {105,0x00,0xFF}, {106,0x80,0xBF}, {107,0x00,0xFF}, {108,0x00,0x7F}, {109,0x00,0xFF}, {110,0x40,0xFF}, {111,0x00,0xFF}, {112,0x00,0x7F}, {113,0x00,0xFF}, {114,0x40,0xFF}, {115,0x00,0xFF}, {116,0x80,0xFF}, {117,0x00,0xFF}, {118,0x40,0xFF}, {119,0x00,0xFF}, {120,0x00,0xFF}, {121,0x00,0xFF}, {122,0x40,0xFF}, {123,0x00,0xFF}, {124,0x00,0xFF}, {125,0x00,0xFF}, {126,0x00,0xFF}, {127,0x00,0xFF}, {128,0x00,0xFF}, {129,0x00,0x0F}, {130,0x00,0x0F}, {131,0x00,0xFF}, {132,0x00,0xFF}, {133,0x00,0xFF}, {134,0x00,0xFF}, {135,0x00,0xFF}, {136,0x00,0xFF}, {137,0x00,0xFF}, {138,0x00,0xFF}, {139,0x00,0xFF}, {140,0x00,0xFF}, {141,0x00,0xFF}, {142,0x00,0xFF}, {143,0x00,0xFF}, {144,0x00,0xFF}, {145,0x00,0x00}, {146,0xFF,0x00}, {147,0x00,0x00}, {148,0x00,0x00}, {149,0x00,0x00}, {150,0x00,0x00}, {151,0x00,0x00}, {152,0x00,0xFF}, {153,0x00,0xFF}, {154,0x00,0xFF}, {155,0x00,0xFF}, {156,0x00,0xFF}, {157,0x00,0xFF}, {158,0x00,0x0F}, {159,0x00,0x0F}, {160,0x00,0xFF}, {161,0x00,0xFF}, {162,0x00,0xFF}, {163,0x00,0xFF}, {164,0x00,0xFF}, {165,0x00,0xFF}, {166,0x00,0xFF}, {167,0x00,0xFF}, {168,0x00,0xFF}, {169,0x00,0xFF}, {170,0x00,0xFF}, {171,0x00,0xFF}, {172,0x00,0xFF}, {173,0x00,0xFF}, {174,0x00,0xFF}, {175,0x00,0xFF}, {176,0x00,0xFF}, {177,0x00,0xFF}, {178,0x00,0xFF}, {179,0x00,0xFF}, {180,0x00,0xFF}, {181,0x00,0x0F}, {182,0x00,0xFF}, {183,0x00,0xFF}, {184,0x00,0xFF}, {185,0x00,0xFF}, {186,0x00,0xFF}, {187,0x00,0xFF}, {188,0x00,0xFF}, {189,0x00,0xFF}, {190,0x00,0xFF}, {191,0x00,0xFF}, {192,0x00,0xFF}, {193,0x00,0xFF}, {194,0x00,0xFF}, {195,0x00,0xFF}, {196,0x00,0xFF}, {197,0x00,0xFF}, {198,0x00,0xFF}, {199,0x00,0xFF}, {200,0x00,0xFF}, {201,0x00,0xFF}, {202,0x00,0xFF}, {203,0x00,0x0F}, {204,0x00,0xFF}, {205,0x00,0xFF}, {206,0x00,0xFF}, {207,0x00,0xFF}, {208,0x00,0xFF}, {209,0x00,0xFF}, {210,0x00,0xFF}, {211,0x00,0xFF}, {212,0x00,0xFF}, {213,0x00,0xFF}, {214,0x00,0xFF}, {215,0x00,0xFF}, {216,0x00,0xFF}, {217,0x00,0xFF}, {218,0x00,0x00}, {219,0x00,0x00}, {220,0x00,0x00}, {221,0x0D,0x00}, {222,0x00,0x00}, {223,0x00,0x00}, {224,0xF4,0x00}, {225,0xF0,0x00}, {226,0x00,0x00}, {227,0x00,0x00}, {228,0x00,0x00}, {229,0x00,0x00}, {231,0x00,0x00}, {232,0x00,0x00}, {233,0x00,0x00}, {234,0x00,0x00}, {235,0x00,0x00}, {236,0x00,0x00}, {237,0x00,0x00}, {238,0x14,0x00}, {239,0x00,0x00}, {240,0x00,0x00}, {242,0x00,0x02}, {243,0xF0,0x00}, {244,0x00,0x00}, {245,0x00,0x00}, {247,0x00,0x00}, {248,0x00,0x00}, {249,0xA8,0x00}, {250,0x00,0x00}, {251,0x84,0x00}, {252,0x00,0x00}, {253,0x00,0x00}, {254,0x00,0x00}, {255,1,0xFF}, // Установка бита страницы в значение 1. { 0,0x00,0x00}, { 1,0x00,0x00}, { 2,0x00,0x00}, { 3,0x00,0x00}, { 4,0x00,0x00}, { 5,0x00,0x00}, { 6,0x00,0x00}, { 7,0x00,0x00}, { 8,0x00,0x00}, { 9,0x00,0x00}, { 10,0x00,0x00}, { 11,0x00,0x00}, { 12,0x00,0x00}, { 13,0x00,0x00}, { 14,0x00,0x00}, { 15,0x00,0x00}, { 16,0x00,0x00}, { 17,0x01,0x00}, { 18,0x00,0x00}, { 19,0x00,0x00}, { 20,0x90,0x00}, { 21,0x31,0x00}, { 22,0x00,0x00}, { 23,0x00,0x00}, { 24,0x01,0x00}, { 25,0x00,0x00}, { 26,0x00,0x00}, { 27,0x00,0x00}, { 28,0x00,0x00}, { 29,0x00,0x00}, { 30,0x00,0x00}, { 31,0x00,0xFF}, { 32,0x00,0xFF}, { 33,0x01,0xFF}, { 34,0x00,0xFF}, { 35,0x00,0xFF}, { 36,0x90,0xFF}, { 37,0x31,0xFF}, { 38,0x00,0xFF}, { 39,0x00,0xFF}, { 40,0x01,0xFF}, { 41,0x00,0xFF}, { 42,0x00,0xFF}, { 43,0x00,0x0F}, { 44,0x00,0x00}, { 45,0x00,0x00}, { 46,0x00,0x00}, { 47,0x00,0xFF}, { 48,0x00,0xFF}, { 49,0x01,0xFF}, { 50,0x00,0xFF}, { 51,0x00,0xFF}, { 52,0x90,0xFF}, { 53,0x31,0xFF}, { 54,0x00,0xFF}, { 55,0x00,0xFF}, { 56,0x01,0xFF}, { 57,0x00,0xFF}, { 58,0x00,0xFF}, { 59,0x00,0x0F}, { 60,0x00,0x00}, { 61,0x00,0x00}, { 62,0x00,0x00}, { 63,0x00,0xFF}, { 64,0x00,0xFF}, { 65,0x01,0xFF}, { 66,0x00,0xFF}, { 67,0x00,0xFF}, { 68,0x90,0xFF}, { 69,0x31,0xFF}, { 70,0x00,0xFF}, { 71,0x00,0xFF}, { 72,0x01,0xFF}, { 73,0x00,0xFF}, { 74,0x00,0xFF}, { 75,0x00,0x0F}, { 76,0x00,0x00}, { 77,0x00,0x00}, { 78,0x00,0x00}, { 79,0x00,0xFF}, { 80,0x00,0xFF}, { 81,0x00,0xFF}, { 82,0x00,0xFF}, { 83,0x00,0xFF}, { 84,0x90,0xFF}, { 85,0x31,0xFF}, { 86,0x00,0xFF}, { 87,0x00,0xFF}, { 88,0x01,0xFF}, { 89,0x00,0xFF}, { 90,0x00,0xFF}, { 91,0x00,0x0F}, { 92,0x00,0x00}, { 93,0x00,0x00}, { 94,0x00,0x00}, {255,0,0xFF} // Установка бита страницы в значение 0. }; См. файл Si5351A-proj\Si5351A-RevB-Regmap.h из архива [6]. 4.5. Программирование Si5338 MCU и Si5338 в момент старта получают питание одновременно, при этом в спроектированной системе должны быть обеспечены входные тактовые сигналы для MCU и Si5338. MCU выполнит свою инициализацию быстрее, чем Si5338; таким образом, в firmware MCU реализована задержка, чтобы поставить на паузу выполнение кода примерно на 12 мс. После этого MCU начнет обрабатывать данные массива Reg_Store. В результате через I2C будет происходить прямая запись значений в те регистры, у которых маска данных равна 0xFF, и для других значений масок будет выполняться операция read-modify-write, что обеспечит сохранение оригинальных значений некоторых бит. Исходный код C8051F301, который выполняет эти действия, см. во врезке приложения. [5. Что можно улучшить] • Настройка холодного старта Si5338. Микросхема Si5338 содержит энергонезависимую память (non-volatile memory, NVM), которую можно настроить для запуска Si5338. Эта возможность позволяет использовать Si5338 для генерации тактовой частоты MCU, в результате снижается количество настроек, который должен делать MCU при своем старте. Настройки холодного старта Si5338 прописываются на заводе, для чего необходимо связаться с Silicon Labs. Требования к этому примеру кода: • C8051F300DK development kit, который имеет в своем составе Silabs IDE, оценочную версию компилятора Keil и USB Debug Adapter. Рекомендуемая документация: • C8051F30x Data Sheet //----------------------------------------------------------------------------- // F300_JumpStart.c //----------------------------------------------------------------------------- // Copyright 2010 Silicon Laboratories, Inc. // http://www.silabs.com // // Описание программы: // // - Реализация шины SMBus под управлением прерываний // - Определены состояния только master (нет арбитража или отслеживание // состояния slave) // - Timer1 используется в качестве источника тактов SMBus // - Timer2 используется SMBus для детектирования таймаута лог. 0 SCL // - Частота SCL определяется константой < SMB_FREQUENCY> // - Адрес для slave-устройства I2C определен константой SLAVE_ADDR // - Реализована поддержка ARBLOST // - Используемые порты (все другие ножки портов не задействованы): // P0.0 -> SDA (SMBus) // P0.1 -> SCL (SMBus) // // Целевой MCU: C8051F301 // Tool chain: Keil 7.20 // // Release 1.0 // - Начальная версия (ACA) // - Июнь 2009 // // Release 1.1 // - Адрес I2C перенесен в этот файл из register_map.h // - Этот проект работает с новой генерацией файла register_map.h // в утилите Multisynth Clock Programmer // // Release 1.2 (август 2010) // - Добавлен тест для ситуации, когда mask = 0x00, чтобы пропустить // обращение к этому регистру. // - Обновлен список масок регистров Si5338 в файле register_map.h // // Release 1.3 (сентябрь 2010) // - Обновлен файл register_map.h и main(), чтобы отразить изменения // в даташите Si5338 // //----------------------------------------------------------------------------- // Подключаемые файлы //----------------------------------------------------------------------------- #include < compiler_defs.h> #include < c8051F300_defs.h> // декларации SFR #include < register_map.h> //----------------------------------------------------------------------------- // Глобальные константы //----------------------------------------------------------------------------- // Адрес устройства (7 бит) для подчиненного устройства I2C. // Для Si5338 адрес по умолчанию 0x70, для Si5351 адрес по умолчанию 0x60. #define SLAVE_ADDR 0x70 #define SYSCLK 24500000 // Системная тактовая частота в Гц. #define SMB_FREQUENCY 400000 // Скорость передачи I2C (частота тактов SCL) // может быть 100 кГц или 400 кГц. #define WRITE 0x00 // Команда SMBus WRITE #define READ 0x01 // Команда SMBus READ // Вектор статуса - только 4 старшие бита #define SMB_MTSTA 0xE0 // (MT) передан start #define SMB_MTDB 0xC0 // (MT) передан байт данных #define SMB_MRDB 0x80 // (MR) принят байт данных // Конец определений вектора состояния //#define SI5338_DELAY 4800 //2 мс //#define SI5338_DELAY 24000 //10 мс #define SI5338_DELAY 28800 //12 мс #define LOCK_MASK 0x15 #define LOS_MASK 0x04 //----------------------------------------------------------------------------- // Глобальные переменные //----------------------------------------------------------------------------- U8* pSMB_DATA_IN; // Глобальный указатель на данные SMBus. // Сюда записываются все принятые данные. U8 SMB_SINGLEBYTE_OUT; // Глобальное хранилище для одного записываемого байта. U8* pSMB_DATA_OUT; // Глобальный указатель на данные SMBus. // Отсюда берутся все передаваемые данные. U8 SMB_DATA_LEN; // Глобальное хранилище для количества байт для отправки // или приема в текущей пересылке данных SMBus. U8 WORD_ADDR; // Глобальное хранилище для слова адреса, к которому // будет осуществляться доступ при следующей пересылке. U8 TARGET; // Адрес целевого подчиненного устройства SMBus. volatile bit SMB_BUSY = 0; // Программный флаг, показывающий задействование // функций I2C_ByteRead() или I2C_ByteWrite() // шиной SMBus. bit SMB_RW; // Программный флаг, показывающий направление // текущего перемещения данных. bit SMB_SENDWORDADDR; // Установка этого флага приводит к отправке // в коде ISR 8-битного < WORD_ADDR> после отправки // адреса подчиненного устройства. bit SMB_RANDOMREAD; // Установка этого флага приводит к отправке кодом ISR // сигнала START после отправки слова адреса. // ISR обрабатывает это изменение, если установлен // бит < SMB_RANDOMREAD>. bit SMB_ACKPOLL; // Установка этого флага приводит к отправке кодом ISR // повторного START до тех пор, пока подчиненное устройство // не подтвердит свой адрес. SBIT(SDA, SFR_P0, 0); // SMBus на P0.0 SBIT(SCL, SFR_P0, 1); // и P0.1 SBIT (P0_5, SFR_P0, 5); SBIT (P0_6, SFR_P0, 6); //----------------------------------------------------------------------------- // Прототипы функций //----------------------------------------------------------------------------- void SMBus_Init (void); void Timer1_Init (void); void Timer2_Init (void); void Port_Init (void); INTERRUPT_PROTO(SMBus_ISR, INTERRUPT_SMBUS0); INTERRUPT_PROTO(Timer2_ISR, INTERRUPT_TIMER2); void I2C_ByteWrite (U8 addr, U8 dat); U8 I2C_ByteRead (U8 addr); //----------------------------------------------------------------------------- // Основное тело программы //----------------------------------------------------------------------------- void main (void) { U16 counter; U8 curr_chip_val, clear_curr_val, clear_new_val, combined, reg; Reg_Data curr; U8 i; // Переменная временного счетчика, используемая в циклах. PCA0MD &= ~0x40; // WDTE = 0 (запрет таймера watchdog) OSCICN |= 0x03; // Конфигурирование внутреннего генератора на его // максимальную частоту (24.5 МГц). // Цикл, если подчиненное устройство удерживает SDA=0 из-за неправильного // сброса SMBus reset или ошибки: while(!SDA) { // Предоставление тактовых импульсов, чтобы позволить подчиненному // устройству выйти из своего текущего состояния и освободить SDA. XBR1 = 0x40; // Enable Crossbar SCL = 0; // Перевести тактовый сигнал в 0 for(i = 0; i < 255; i++); // Удержание в нуле сигнала тактов SCL = 1; // Освобождение тактов. while(!SCL); // Ожидание размыкания открытого стока тактов, // чтобы сигнал тактов перешел в лог. 1. for(i = 0; i < 10; i++); // Удержание тактового сигнала не некоторое // время в лог. 1. XBR1 = 0x00; // Disable Crossbar } Port_Init (); // Инициализация Crossbar и GPIO. CKCON = 0x10; // Timer 1 тактируется от sysclk. // Timer 2 тактируется от sysclk/12 (см. TMR2CN). Timer1_Init (); // Конфигурирование Timer1 в качестве источника // тактов для SMBus. Timer2_Init () // Конфигурирование Timer2 в качестве источника // тактов отслеживания таймаута SMBus. SMBus_Init (); // Конфигурирование и разрешение работы SMBus. EIE1 |= 0x01; // Разрешение прерывания SMBus. EA = 1; // Глобальное разрешение прерываний (это разрешение // прерываний должно быть последним!). //---------------------------------------------------------------- // См. рис. 9 из даташита Si5338, для получения большей информации // по задержке этой процедуры, чтобы подождать готовности Si5338 // к обмену данными после включения питания. counter = 0; while(counter < SI5338_DELAY) { counter++; } I2C_ByteWrite(230, 0x10); //OEB_ALL = 1 I2C_ByteWrite(241, 0xE5); //DIS_LOL = 1 // Для всех значений регистров в массиве Reg_Store будет выбрано // value и mask, и применено к Si5338: for(counter=0; counter < NUM_REGS_MAX; counter++) { curr = Reg_Store[counter]; if(curr.Reg_Mask != 0x00) { if(curr.Reg_Mask == 0xFF) { // Выполнение транзакции записи только тогда, когда в // маске все единицы: do a write transaction only since the mask is all ones I2C_ByteWrite(curr.Reg_Addr, curr.Reg_Val); } else { // Иначе будет выполнена операция read-modify-write: curr_chip_val = I2C_ByteRead(curr.Reg_Addr); clear_curr_val = curr_chip_val & ~curr.Reg_Mask; clear_new_val = curr.Reg_Val & curr.Reg_Mask; combined = clear_new_val | clear_curr_val; I2C_ByteWrite(curr.Reg_Addr, combined); } } } // Проверка ошибки LOS для входа XTAL на IN1 и IN2 (и IN3, если // это необходимо) - поменяйте эту маску, если используете // входы на IN4, IN5, IN6: reg = I2C_ByteRead(218) & LOS_MASK; while(reg != 0) reg = I2C_ByteRead(218) & LOS_MASK; I2C_ByteWrite(49, I2C_ByteRead(49) & 0x7F); //FCAL_OVRD_EN = 0 I2C_ByteWrite(246, 2); //soft reset I2C_ByteWrite(241, 0x65); //DIS_LOL = 0 // Ожидание готовности Si5338 после внутренней калибровки // (например, вызванной soft reset): counter = 0; while(counter < SI5338_DELAY) { counter++; } counter = 0; while(counter < SI5338_DELAY) { counter++; } // Убедимся, что произошел захват ФАПЧ проверкой PLL_LOL и SYS_CAL: reg = I2C_ByteRead(218) & LOCK_MASK; while(reg != 0) reg = I2C_ByteRead(218) & LOCK_MASK; // Копирование значений FCAL: I2C_ByteWrite(45, I2C_ByteRead(235)); I2C_ByteWrite(46, I2C_ByteRead(236)); // Очистка бит 0 и 1 из регистра 47, и комбинирование их // с битами 0 и 1 из регистра 237: reg = (I2C_ByteRead(47) & 0xFC) | (I2C_ByteRead(237) & 3); I2C_ByteWrite(47, reg); I2C_ByteWrite(49, I2C_ByteRead(49) | 0x80); // FCAL_OVRD_EN = 1 I2C_ByteWrite(230, 0x00); // OEB_ALL = 0 //------------------------------------------------------------ // Бесконечное ожидание: while(1); // Закомментируйте эту строку и раскомментируйте код ниже, // если используется опция выключения (power-down) MCU. /* // Выключение MCU: while(SMB_BUSY); // Ожидание завершения транзакций I2C. EA = 0; // Выключение прерываний. SMB0CF &= ~0x80; // Выключение SMBus. RSTSRC = 0x00; // Выключение источника сброса MCD. PCON = 0x02; // Остановка MCU - из этого состояния его выведет только reset. */ } //----------------------------------------------------------------------------- // Подпрограммы инициализации //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // SMBus_Init //----------------------------------------------------------------------------- // Значение возврата: нет // Параметры: нет // // Периферийного устройство SMBus конфигурируется следующим образом: // - Разрешается SMBus. // - Запрещается режим Slave. // - Timer1 используется в качестве источника тактов. Максимальная частота // SCL будет примерно 1/3 от частоты переполнения Timer1. // - Разрешаются расширения времени установки и удержания (setup and hold time). // - Разрешается определение таймаута освобождения SMBus и таймаута SCL==0. void SMBus_Init (void) { SMB0CF = 0x5D; // Переполнение Timer1 используется в качестве тактов SMBus; // Запрет slave mode; // Разрешение расширений времени setup & hold; // Разрешение SMBus Free timeout detect; // Разрешение SCL low timeout detect; SMB0CF |= 0x80; // Разрешение SMBus; } //----------------------------------------------------------------------------- // Timer1_Init //----------------------------------------------------------------------------- // Значение возврата: нет // Параметры: нет // // Timer1 конфигурируется в качестве источника тактов SMBus следующим образом: // - Timer1 в режиме 8-bit auto-reload. // - SYSCLK / 12 в качестве источника тактов для Timer1. // - Частота переполнения Timer1 => 3 * SMB_FREQUENCY // - Максимальная частота тактов SCL будет примерно ~1/3 от частоты // переполнения Timer1. // - Разрешается работа Timer1. void Timer1_Init (void) { // Гарантируем, что Timer1 может генерировать подходящую частоту в 8-битном режиме. // Поддерживаемые SMBus частоты лежат в диапазоне 10 .. 100 кГц. Могут потребоваться // изменения установок CKCON, для частот вне этого диапазона. TMOD = 0x20; // Timer1 в режиме 8-bit auto-reload. TH1 = 0xFF - (SYSCLK/SMB_FREQUENCY/3) + 1; // 100 кГц или 400 кГц для SCL. TL1 = TH1; // Инициализация Timer1. TR1 = 1; // Разрешение Timer1. } //----------------------------------------------------------------------------- // Timer2_Init //----------------------------------------------------------------------------- // Значение возврата: нет // Параметры: нет // // Timer2 конфигурируется для детектирования таймаута SCL==0 следующим образом: // - Timer2 в режиме 16-bit auto-reload // - SYSCLK/12 в качестве источника тактов Timer2 void Timer2_Init (void) { TMR2CN = 0x00; // Timer2 конфигурируется для 16-bit auto-reload, // запрет прерывания от младшего байта. // Timer2 использует SYSCLK/12 (см. CKCON). TMR2RLL = 0x5F; TMR2RLH = 0x88; TMR2L = TMR2RLL; TMR2H = TMR2RLH; TF2LEN = 0; TF2H = 0; TF2L = 0; ET2 = 1; // Разрешение прерывания Timer2. TR2 = 1; // Запуск Timer2. } //----------------------------------------------------------------------------- // Port_Init //----------------------------------------------------------------------------- // Значение возврата: нет // Параметры: нет // // Конфигурирование Crossbar и портов GPIO. // // P0.0 цифровой порт с открытым стоком для сигнала SMBus SDA // P0.1 цифровой порт с открытым стоком для сигнала SMBus SCL // // Все другие ножки портов не используются. void Port_Init (void) { P0MDOUT = 0x00; // Все выводы P0 работают как выходы с общим стоком. XBR1 = 0x04; // Разрешение SMBus. XBR2 = 0x40; // Разрешение Crossbar и внутренних "слабых" верхних // подтягивающих резисторов (pull-up). P0 = 0xFF; // Явная установка всех выходов в лог. 1. } //----------------------------------------------------------------------------- // Обработчик прерывания SMBus (Interrupt Service Routine, ISR) //----------------------------------------------------------------------------- // Машина состояния SMBus ISR // - Реализован только режим Master - не определены состояния slave или арбитража. // - Все приходящие данные записываются, начиная с глобального указателя < pSMB_DATA_IN>. // - Все уходящие данные берутся из глобального указателя < pSMB_DATA_OUT>. INTERRUPT(SMBus_ISR, INTERRUPT_SMBUS0) { bit FAIL = 0; // Используется ISR для ошибок при перемещении данных. static char i; // Используется ISR для подсчета переданных или // отправленных байт данных. static bit SEND_START = 0; // Отправка start. switch (SMB0CN & 0xF0) // Анализ вектора состояния { case SMB_MTSTA: // Master Transmitter/Receiver: передан сигнал START. SMB0DAT = TARGET; // Загрузка адреса подчиненного устройства. SMB0DAT &= 0xFE; // Очистка LSB в адресе для бита R/W. SMB0DAT |= SMB_RW; // Загрузка бита R/W. STA = 0; // Ручная очистка бита START. i = 0; // Сброс счетчика байт данных. break; case SMB_MTDB: // Master Transmitter: передан байт данных (или адрес Slave). if (ACK) // Slave Address или Data Byte { // Было подтверждение? if (SEND_START) { STA = 1; SEND_START = 0; break; } if(SMB_SENDWORDADDR) // Мы отправляем адрес слова? { SMB_SENDWORDADDR = 0; // Очистка флага SMB0DAT = WORD_ADDR; // Отправка слова адреса if (SMB_RANDOMREAD) { SEND_START = 1; // Отправка START после следующего цикла ACK SMB_RW = READ; } break; } if (SMB_RW==WRITE) // WRITE? { if (i < SMB_DATA_LEN) // Есть данные для отправки? { // отправка байта данных SMB0DAT = *pSMB_DATA_OUT; // инкремент указателя на уходящие данные pSMB_DATA_OUT++; // инкремент количества отправленных данных i++; } else { STO = 1; // Установка STO, чтобы остановить передачу SMB_BUSY = 0; // Очистка флага программной занятости } } else {} // Если это READ, то не предпринимается // никаких действий. Был передан адрес Slave. // Определена отдельная ветка case для // принятого байта. } else // Подчиненное устройство ответило NACK { if(SMB_ACKPOLL) { STA = 1; // Перезапуск перемещения данных } else { FAIL = 1; // Показать ошибку перемещения данных } // для обработки в конце ISR. } break; case SMB_MRDB: // Master Receiver: принят байт if ( i < SMB_DATA_LEN ) // Нужно еще принимать данные? { *pSMB_DATA_IN = SMB0DAT; // Сохранение принятого байта. pSMB_DATA_IN++; // Инкремент указателя на принимаемые данные. i++; // Инкремент количества принятых байт. ACK = 1; // Установка бита ACK (может быть позже // очищен в этом коде). } if (i == SMB_DATA_LEN) // Это последний байт { ACK = 0; // Отправка NACK, чтобы показать последний байт // этой передачи. STO = 1; // Отправка STOP для завершение передачи. SMB_BUSY = 0; // Освобождение интерфейса SMBus. } break; default: FAIL = 1; // Показать ошибку, чтобы обработать это // в конце ISR. break; } if (FAIL) // Если перемещение потерпело ошибку, { SMB0CF &= ~0x80; // Сброс обмена. SMB0CF |= 0x80; STA = 0; STO = 0; ACK = 0; FAIL = 0; SMB_BUSY = 0; // Освобождение SMBus. } SI = 0; // Очистка флага прерываний. } //----------------------------------------------------------------------------- // Обработчик прерываний Timer2 (Interrupt Service Routine, ISR) //----------------------------------------------------------------------------- // Прерывание Timer2 показывает таймаут SMBus события SCL==0. // Если произошел этот таймаут, то SMBus запрещается и разрешается заново. INTERRUPT(Timer2_ISR, INTERRUPT_TIMER2) { SMB0CF &= ~0x80; // Запрет SMBus. SMB0CF |= 0x80; // Повторное разрешение SMBus. TF2H = 0; // Очистка флага ожидания обработки прерывания Timer2. SMB_BUSY = 0; // Освобождение шины. } //----------------------------------------------------------------------------- // Функции поддержки //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // I2C_ByteWrite //----------------------------------------------------------------------------- // Значение возврата: нет // Параметры: // 1) unsigned char addr - адрес для записи в устройство через I2C // (может быть в диапазоне 0 .. 255). // // 2) unsigned char dat - данные для записи в устройство по адресу < addr> // (может быть в диапазоне 0 .. 255). // // Эта функция записывает значение dat в устройство по адресу addr, // после чего опрашивает устройство, пока запись не завершится. void I2C_ByteWrite (U8 addr, U8 dat) { while (SMB_BUSY); // Ожидание освобождения SMBus. SMB_BUSY = 1; // Занятие SMBus (установка состояния занятости) // Установка параметров SMBus для ISR TARGET = SLAVE_ADDR << 1; // Установка целевого адреса подчиненного устройства SMB_RW = WRITE; // Пометка следующей транзакции как записи. SMB_SENDWORDADDR = 1; // Отправка Word Address после Slave Address. SMB_RANDOMREAD = 0; // Не отправлять сигнал START после Word Address. SMB_ACKPOLL = 1; // Разрешение опроса подтверждения (ISR будет // автоматически перезапускать транзакцию, если // подчиненное устройство не подтвердило свой адрес). // Указание исходящих данных WORD_ADDR = addr; // Установка целевого адреса во внутреннем // пространстве памяти устройства. SMB_SINGLEBYTE_OUT = dat; // Сохранение dat (локальная переменная) в глобальную // переменную, чтобы ISR смог прочитать её после того, // как произойдет выход из этой функции. // Указатель на уходящие данные указывает на переменную dat pSMB_DATA_OUT = &SMB_SINGLEBYTE_OUT; SMB_DATA_LEN = 1; // Указать для ISR, что следующая передача // будет содержать 1 байт данных. // Инициирование SMBus Transfer STA = 1; } //----------------------------------------------------------------------------- // I2C_ByteRead //----------------------------------------------------------------------------- // Возвращаемое значение: // 1) unsigned char data - данные, прочитанные по адресу addr в устройстве. // // Параметры: // 1) unsigned char addr - адрес для чтения данных из устройства. // Может быть в диапазоне 0 .. 255. // // Эта функция вернет 1 байт из внутренней памяти устройства по адресу addr, // когда флаг < SMB_BUSY> покажет завершения чтения. U8 I2C_ByteRead (U8 addr) { U8 return_val; // Переменная для хранения возвращаемого значения. while (SMB_BUSY); // Ожидание освобождения SMBus. SMB_BUSY = 1; // Занятие SMBus (установка состояния занятости). // Установка параметров SMBus для ISR TARGET = SLAVE_ADDR << 1; // Установка целевого адреса подчиненного устройства SMB_RW = WRITE; // Чтение по произвольному адресу начинается как запись, // затем меняется на чтение после отправки повторного // start. ISR обрабатывает это переключение, если // установлен бит < SMB_RANDOMREAD>. SMB_SENDWORDADDR = 1; // Отправка Word Address после Slave Address. SMB_RANDOMREAD = 1; // Отправка START после Word Address. SMB_ACKPOLL = 1; // Разрешение опроса подтверждения. // Указание приходящих данных WORD_ADDR = addr; // Установка целевого адреса во внутренней памяти // устройства. pSMB_DATA_IN = &return_val;// Указатель приходящих данных указывает на переменную // return_val. SMB_DATA_LEN = 1; // Указывает ISR, что следующее перемещение данных // будет содержать 1 байт данных. // Инициация SMBus Transfer STA = 1; while(SMB_BUSY); // Ожидание завершения чтения данных. return return_val; } [Ссылки] 1. AN428 site:silabs.com. |