В этой документации (перевод [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:
• Физический размер корпуса. • Размер памяти. • Последовательный интерфейс для связи с целевыми конфигурируемыми устройствами (I2C или SPI). • Количество цифровых выводов портов.
Есть возможность перейти на однократно программируемые 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 даташита). 2. У микроконтроллера F301 есть 8 ножек цифровых порта I/O, сконфигурированных как выходы с открытым стоком и внутренне подключенными к ним слабыми верхними подтягивающими резисторами (weak pull-up). Изначально все сигналы устанавливаются в состояние лог. 1. 3. Машина состояний I2C master адаптирована из документа "AN141: SMBUS Communication for Small Form Factor Device Families" для использования вместе с F301 (см. раздел "6.3 EEPROM Example" в AN141). SDA подключен к ножке порта P0.0, и SCL к ножке порта P0.1 MCU при использовании SMBus (другое название шины I2C). Timer 1 конфигурируется для источника тактов SMBus, и эта частота устанавливается на скорость 400 kbps. Timer 2 конфигурируется для определения события таймаута, которое SMBus использует автоматически в работе своей машины состояний. Используйте этот таймер при необходимости, потому что эта функция не обязательна (для дополнительной информации см. секцию 13.4.1 в даташите C8051F30x и секцию 3.1.2 даташита AN141). 4. Адрес I2C Si5338 устанавливается в значение по умолчанию 0x70 (шестнадцатеричный формат). Функции I2C чтения и записи будут выполнять левый сдвиг адреса и корректное заполнение бита I2C R/W.
4.2. Кодирование Register Map
В этом примере используется кит Si5338-EVB и утилита ClockBuilder Desktop для разработки конфигурации плана частот и сохранения его в текстовый файл register map. В результате получается файл с расширением *.txt, где каждая строка имеет формат:
Примечание: более новая версия 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, конфигурацию входов, расширения выходного спектра, а также настройки частоты и фазы формируемых выходных тактов. • Адресацию бит, регистров и страниц регистров (чтобы обработать ситуацию, когда адреса регистров больше 255). • Вычисленное значение NUM_REGS_MAX.
Имейте в виду, что сгенерированный заголовочный файл не оптимизирован по содержимому в с сравнении с данными настроек, которые могли бы быть созданы вручную, когда для настройки используется только ограниченное подмножество регистров. Например, если нет необходимости менять настройки расширения спектра и фазы, автоматически сгенерированный файл будет занимать больше места в памяти программ, чем необходимо в действительности.
См. файл 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. • Включение питания, управляемое MCU. В этом варианте применения MCU подает команду на включение питания Si53xx, что может понадобиться в приложениях, где есть особые требования по энергопотреблению. • Несколько частотных планов. В зависимости от целей приложения MCU может перенастраивать частоты, генерируемые Si53xx. Выбор загружаемого частотного плана может осуществляться различными способами, например путем опроса перемычек при включении питания. • Несколько подчиненных устройств I2C (или SPI). MCU может посылать индивидуальные данные регистров на несколько подчиненных устройств, подключенных к одной последовательной шине (I2C или SPI). В случае использования I2C выбор нужного подчиненного устройства осуществляется адресацией, предусмотренной в протоколе шины I2C (slave address), что должно быть учтено в firmware микроконтроллера. Почти все микросхемы Si53xx имеют внешние выводы для установки младших бит своего адреса. Например, синтезатор Si5351 [5] имеет для этой цели вывод A0. Для микросхем, подключаемых через SPI, используется специальный сигнал выборки подчиненного устройства (slave select, SS), для которого нужны дополнительные цифровые порты ввода/вывода I/O MCU. При большом количестве внешних подчиненных устройств SPI для выборки может использоваться микросхема дешифратора адреса.
• C8051F300DK development kit, который имеет в своем составе Silabs IDE, оценочную версию компилятора Keil и USB Debug Adapter. • Кит Si5338-EVB, включающий ClockBuilder Desktop Software и EVB.
Рекомендуемая документация:
• C8051F30x Data Sheet • Si5338 Data Sheet • AN141: SMBUS Communication for Small Form Factor Device Families • C8051F30x Development Kit User's Guide • Si5330/34/38 Evaluation Board User's Guide
//-----------------------------------------------------------------------------// 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);
//-----------------------------------------------------------------------------// Прототипы функций//-----------------------------------------------------------------------------voidSMBus_Init (void);
voidTimer1_Init (void);
voidTimer2_Init (void);
voidPort_Init (void);
INTERRUPT_PROTO(SMBus_ISR, INTERRUPT_SMBUS0);
INTERRUPT_PROTO(Timer2_ISR, INTERRUPT_TIMER2);
voidI2C_ByteWrite (U8 addr, U8 dat);
U8 I2C_ByteRead (U8 addr);
//-----------------------------------------------------------------------------// Основное тело программы//-----------------------------------------------------------------------------voidmain (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; // Перевести тактовый сигнал в 0for(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.voidSMBus_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.voidTimer1_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 в качестве источника тактов Timer2voidTimer2_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//// Все другие ножки портов не используются.voidPort_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 для ошибок при перемещении данных.staticchar 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. 2. AN136: Production Programming Options for Silicon Labs Devices site:silabs.com. 3. Parametric Search site:silabs.com. 4. Si5338 site:silabs.com. 5. Si5351: программируемый генератор на любую частоту. 6. 180524an428.ZIP - документация, дистрибутив ClockBuilder Pro 2.23, пример заголовочного файла для программирования Si5351A (проект ClockBuilder Pro).