Итак, запускаем среду разработки Atmel Studio, выбираем контроллер ATSAM4Cxx_0 и программатор в опции device и tool соответственно.
Тактирование
Тактовый сигнал жизненно необходим для любого контроллера, он задает точный ритм работы, благодаря которому все команды и процессы выполняются синхронно. Первое, что нужно сделать при разработке программы для Cortex-M4, да и вообще для ARM — это настроить систему тактирования.
Она управляется PMC — контроллером питания, как ни странно.
Пока, я полагаю, мало что понятно. Начну с того, что вся периферия и сам ЦП работают от главного MaterClock-а, при этом мы можем выбрать что тактировать, а что — нет. В архитектуре ARM такая логика: нет clock-а — нет проблем! Поэтому при подачи питания большая часть периферии не работает.
Источником для MasterClock может являться резонатор/генератор, а его частота еще до MaterClock-а всячески преобразовывается системой тактирования, что позволяет нам увеличивать или уменьшать частоту всего МК программно. Рассмотрим первый блок:
Возможных путей преобразования сигнала с генераторов не мало.
Переключатели просто выбирают источник частоты, а с PLL или ФАПЧ дела обстоят интереснее. У каждого блока PLL есть два регистра: MUL и DIV. MUL — задает коэффициент умножения частоты на входе, а DIV ее последующее деление. То есть, формула выходной частоты блока будет такой: fвых = (fвх * MUL)/DIV Естественно, MUL и DIV не должны равняться нулю, иначе PLL блок просто отключится.
В итоге мы получаем сразу четыре разных clock-а:
- SLCK — всегда ~32KHz
- MAINCK — выбирается по-умолчанию после сброса, от 4 до 20MHz
- PLLACK — выходная частота с первого блока умножения, от 1 до 8MHz возможно.
- PLLBCK — выходная частота со второго блока, от 1 до 240MHz возможно, но контроллер не будет работать выше 120MHz.
Они поступают во вторую часть:
Здесь система ограничивается мультиплексором и делителем. Мы может лишь выбрать один из четырех тактовых сигналов в качестве MasterClock-а, поделив при желании. Так как МК у нас двух ядреный, на каждый процессор и часть периферии приходится по одной такой конструкции.
Не маловажным является контроллер периферии, функция которого отключение тактирования того или иного модуля.
1 2 |
PMC->PMC_PCER0 = НомерПериферийногоУстройства; //Включить тактирование PMC->PMC_PCDR0 = НомерПериферийногоУстройства; //Выключить тактирование |
Настройка
Вся настройка контроллера питания происходит путем записи данных в его регистры. Разобраться в том, что и куда записывать вам поможет встроенная в среду CMSIS библиотека с подсказками и изображения выше.
Для начала разблокируем регистры:
1 |
PMC->PMC_WPMR = 0x504D43; //Очень черная магия |
Следующий шаг — определиться с рабочей частотой, к примеру, мы хотим аж 120MHz. Как ее получить? Используем часовой кварц и два PLL блока (так советует документация): f = (f32KHz * 250) * 15.
В коде это будет описано так:
1 2 3 4 5 6 7 8 9 |
PMC->CKGR_MOR = CKGR_MOR_MOSCXTEN; //Используем внешний кристалл PMC->CKGR_PLLAR = CKGR_PLLAR_PLLACOUNT(0) | CKGR_PLLAR_PLLAEN(0); //PLLA: Сброс PMC->CKGR_PLLAR = CKGR_PLLAR_MULA(249) | CKGR_PLLAR_PLLAEN(1); //PLLA: Умножить на 250 PMC->CKGR_PLLBR = CKGR_PLLBR_PLLBCOUNT(0); //PLLB: Сброс PMC->CKGR_PLLBR = CKGR_PLLBR_MULB(14) | CKGR_PLLBR_DIVB(1) | CKGR_PLLBR_SRCB_PLLA_IN_PLLB; //PLLB: Умножить на 15, делить на 1, источник — PLLA. PMC->PMC_MCKR = PMC_PCK_CSS_PLLB_CLK | PMC_PCK_PRES_CLK_1; //MasterClock: Источник — PLLB, Делить на 1. SystemCoreClockUpdate(); //Обновим главную переменную, используемую для задержек и прочих. |
Последняя строчка обновляет переменную SystemCoreClock (uint32_t), в которой будет десятичное значение текущей частоты. Однако, я заметил некоторую странность ее поведения, поэтому, если имеются проблемы, то присваивайте вручную:
1 |
SystemCoreClock = 120000000; |
Но у вас, скорее всего, ничего не заработает. Мы забыли очень важную вещь: тактирование flash памяти происходит по-иному: вместо частоты у нее есть параметр — задержка, ее нужно изменить.
1 |
system_init_flash(120000000); |
Ставьте эту строчку до инициализации системы тактирования.
Проверка
Для проверки системы тактирования вы можете использовать PCK — программируемый выводной контроллер частоты. Он имеет несколько выходных пинов, на которые мы можем вывести нашу тактовую частоту. Возьмем PCK0 — это PB13.
1 2 3 4 5 6 7 8 9 10 11 12 |
PMC->PMC_PCER0 = PMC_PCER0_PID12; //Включили тактирование PB порта //Настройка порта. Об этом в другой статье. PIOB->PIO_IDR = PIO_IDR_P13; PIOB->PIO_OER = PIO_OER_P13; last = PIOB->PIO_ABCDSR[0]; PIOB->PIO_ABCDSR[0] &= (~PIO_ABCDSR_P13 & last); last = PIOB->PIO_ABCDSR[1]; PIOB->PIO_ABCDSR[1] &= (~PIO_ABCDSR_P13 & last); PIOB->PIO_PDR = PIO_PDR_P13; //Настройка PCK PMC->PMC_PCK[0] = PMC_PCK_CSS_MCK | PMC_PCK_PRES_CLK_1; //Выбираем наш MasterClock и предделитель = 1 PMC->PMC_SCER = PMC_SCER_PCK0; //Разрешить тактирование регистра PCK0 |
Теперь смотрим на наш пин осциллографом:
Если ваш осциллограф не поддерживает такую частоту, то увеличьте предделитель PCK.