реклама на сайте
подробности

 
 
> Не запускается PLL при переходе от HSE к HSI, после Syst Reset - РЕШЕНО
Sergey_Aleksandr...
сообщение Sep 28 2012, 14:18
Сообщение #1


Частый гость
**

Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764



Наткнулся на багу в написанной ранее пол-года назад функции инициализации PLL. Процессор STM32F105 (connectivity line с расширенной системой тактирования относительно value line и пр.). Функцию SystemInit не использую (отключил в "startup_stm32f10x_cl.s"). Такой вот код
CODE

int InitRCC(void)
{
unsigned int StartUpCounter = 0;
int Result = 0;

// Конфигурацяи SYSCLK, HCLK, PCLK2 и PCLK1

// Включить HSI. После сброса он и так включен и в программе я его не выключаю, но всё же...
RCC->CR |= RCC_CR_HSION;
while (!(RCC->CR & RCC_CR_HSIRDY));
// Переключить на тактирование от HSI (после сброса оно и так HSI, но тем не менее)
RCC->CFGR &= ~RCC_CFGR_SW;
// Ожидаем, пока настройка вступит в силу
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI);

// Выключить все 3 PLL (после сброса они выключены, но тем не менее)
RCC->CR &= ~(RCC_CR_PLLON | RCC_CR_PLL2ON | RCC_CR_PLL3ON);
while ((RCC->CR & (RCC_CR_PLLRDY | RCC_CR_PLL2RDY | RCC_CR_PLL3RDY)) != 0); //Ждать остановки

// Сбросить делители AHB, APB1, APB2 в исходные состояния
RCC->CFGR &= ~(RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2); //После сброса делитеи установлены в 1, но всё равно вручную ставлю их в 1

// Включить буфер предвыборки FLASH
// (!!!) Включать/отключать Prefetch Buffer только когда SYSCLK ниже 24 MHz и нет предделителей AHB
// т.е. SYSCLK == HCLK <= 24 MHz. См. "STM32F10xxx Flash memory microcontroller Programming Manual PM0075"
FLASH->ACR |= FLASH_ACR_PRFTBE;

/* Конфигурируем Flash на 2 цикла ожидания
Это нужно потому, что Flash не может работать на высокой частоте
если это не сделать, то будет странный глюк. Проц может запуститься, но через пару
секунд повисает без "видимых причин". Вот такие вот неочевидные вилы.*/
if (SYSCLK < 24000000ul) {} //Пропуск тактов при доступе к Flash не надо
else if (SYSCLK < 48000000ul) //На частотах 24-48 МГц пропуск 1 такта
{
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1;
}
else //На частотах 48-72 МГц пропуск 2 тактов
{
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
}




// Если задано тактирование от внешнего кварца HSE (см. <GlobalParams.h> HSE_CLOCK_SOURCE != 0)
if (HSE_CLOCK_SOURCE != 0)
{
// Включить HSE
RCC->CR |= RCC_CR_HSEON;
// Ждем пока HSE не выставит бит готовности либо не выйдет таймаут
do
{
Result = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while( (Result == 0) && (StartUpCounter != HSEStartUp_TimeOut) );



// Если HSE запустился нормально
if (RCC->CR & RCC_CR_HSERDY)
{
Result = 1;

// Конфигурируем множитель PLL configuration. (!) Только при остановленном PLL. Остановил ранее (!)

// Если задано задействовать PPL2
if (PLL2_USE)
{
RCC->CFGR2 |= RCC_CFGR2_PREDIV1SRC; //Тактовый от HSE попадает на PLL1 через PLL2. Менять только при выключенном PLL
// Сбросить биты управления множителя и делителя PLL (после сброса они обнулены, но всё же)
RCC->CFGR2 &= ~(RCC_CFGR2_PLL2MUL | RCC_CFGR2_PREDIV2);
// Множитетель PLL2
if (PLL2_MUL == 8) RCC->CFGR2 |= RCC_CFGR2_PLL2MUL8;
else if (PLL2_MUL == 9) RCC->CFGR2 |= RCC_CFGR2_PLL2MUL9;
else if (PLL2_MUL == 10) RCC->CFGR2 |= RCC_CFGR2_PLL2MUL10;
else if (PLL2_MUL == 11) RCC->CFGR2 |= RCC_CFGR2_PLL2MUL11;
else if (PLL2_MUL == 12) RCC->CFGR2 |= RCC_CFGR2_PLL2MUL12;
else if (PLL2_MUL == 13) RCC->CFGR2 |= RCC_CFGR2_PLL2MUL13;
else if (PLL2_MUL == 14) RCC->CFGR2 |= RCC_CFGR2_PLL2MUL14;
else if (PLL2_MUL == 16) RCC->CFGR2 |= RCC_CFGR2_PLL2MUL16;
else RCC->CFGR2 |= RCC_CFGR2_PLL2MUL20;
// Делитель PLL2
RCC->CFGR2 |= ((PLL2_DIV-1) & 0x0F) << 4; //Биты 7..4 регистра RCC->CFGR2
}

// Сбросить биты управления множителя и делителя PLL (после сброса они обнулены, но всё же)
RCC->CFGR &= ~(RCC_CFGR_PLLMULL | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC);
RCC->CFGR2 &= ~RCC_CFGR2_PREDIV1;
// Множитетель PLL
if (PLL_MUL == 4) RCC->CFGR |= RCC_CFGR_PLLMULL4;
else if (PLL_MUL == 5) RCC->CFGR |= RCC_CFGR_PLLMULL5;
else if (PLL_MUL == 6) RCC->CFGR |= RCC_CFGR_PLLMULL6;
else if (PLL_MUL == 7) RCC->CFGR |= RCC_CFGR_PLLMULL7;
else if (PLL_MUL == 8) RCC->CFGR |= RCC_CFGR_PLLMULL8;
else if (PLL_MUL == 9) RCC->CFGR |= RCC_CFGR_PLLMULL9;
else RCC->CFGR |= RCC_CFGR_PLLMULL6_5;
// Делитель PLL
RCC->CFGR2 |= (PLL_DIV-1) & 0x0F; //4 младших бита регистра RCC->CFGR2

// Подать на вход PLL1 сигнал с HSE
RCC->CFGR |= RCC_CFGR_PLLSRC;
// Включаем PLL1, PLL2, PLL3
RCC->CR |= RCC_CR_PLLON | RCC_CR_PLL2ON | RCC_CR_PLL3ON;
// Ожидать, пока PLL1, PLL2, PLL3 выставит бит готовности
while (!(RCC->CR & (RCC_CR_PLLRDY | RCC_CR_PLL2RDY | RCC_CR_PLL3RDY)));

// Переключаю на тактирование от PLL
RCC->CFGR |= RCC_CFGR_SW_PLL;
// Ожидаем, пока настройка вступит в силу
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
else
{
Result = -1;
/* Все плохо... HSE не завелся... Чего-то с кварцем или еще что...
Надо бы както обработать эту ошибку... Если мы здесь, то мы работаем
от HSI! */
}
} // <------ if (HSE_CLOCK_SOURCE != 0)
// Если задано тактирование от внутреннего RC-генератора (см. <GlobalParams.h> HSE_CLOCK_SOURCE == 0)
else
{
// Сбросить биты управления множителя и делителя PLL (после сброса они обнулены, но всё же)
RCC->CFGR &= ~(RCC_CFGR_PLLMULL | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC);
RCC->CFGR2 &= ~RCC_CFGR2_PREDIV1;
// Множитетель PLL
if (PLL_MUL == 4) RCC->CFGR |= RCC_CFGR_PLLMULL4;
else if (PLL_MUL == 5) RCC->CFGR |= RCC_CFGR_PLLMULL5;
else if (PLL_MUL == 6) RCC->CFGR |= RCC_CFGR_PLLMULL6;
else if (PLL_MUL == 7) RCC->CFGR |= RCC_CFGR_PLLMULL7;
else if (PLL_MUL == 8) RCC->CFGR |= RCC_CFGR_PLLMULL8;
else if (PLL_MUL == 9) RCC->CFGR |= RCC_CFGR_PLLMULL9;
else RCC->CFGR |= RCC_CFGR_PLLMULL6_5;
// Делитель PLL
RCC->CFGR2 |= (PLL_DIV-1) & 0x0F; //4 младших бита регистра RCC->CFGR2

// Подать на вход PLL1 сигнал с HSI - можно не выполнять, т.к. бит RCC_CFGR_PLLSRC уже обнулён
//RCC->CFGR &= ~RCC_CFGR_PLLSRC;
// Включить PLL
RCC->CR |= RCC_CR_PLLON;
// Ожидать, пока PLL1 выставит бит готовности
while (!(RCC->CR & RCC_CR_PLLRDY)); //<--- (!)Вот здесь зависает при переключении от HSE на HSI после системного сброса (!)
// Переключить на тактирование от PLL
RCC->CFGR |= RCC_CFGR_SW_PLL;
// Ожидаем, пока настройка вступит в силу
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);

Result = 1;
}


//--- Делители тактовых частот AHB, APB1, APB2
// Делитеть тактовой частоты на входе AHB, AHB Prescaler
if (AHB_DIV == 1) RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
else if (AHB_DIV == 2) RCC->CFGR |= RCC_CFGR_HPRE_DIV2;
else if (AHB_DIV == 4) RCC->CFGR |= RCC_CFGR_HPRE_DIV4;
else if (AHB_DIV == 8) RCC->CFGR |= RCC_CFGR_HPRE_DIV8;
else if (AHB_DIV == 16) RCC->CFGR |= RCC_CFGR_HPRE_DIV16;
else if (AHB_DIV == 64) RCC->CFGR |= RCC_CFGR_HPRE_DIV64;
else if (AHB_DIV == 128)RCC->CFGR |= RCC_CFGR_HPRE_DIV128;
else if (AHB_DIV == 256)RCC->CFGR |= RCC_CFGR_HPRE_DIV256;
else RCC->CFGR |= RCC_CFGR_HPRE_DIV512;

// Делитель тактовой частоты домена APB1, APB1 Prescaler
if (APB1_DIV == 1) RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
else if (APB1_DIV == 2) RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
else if (APB1_DIV == 4) RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
else if (APB1_DIV == 8) RCC->CFGR |= RCC_CFGR_PPRE1_DIV8;
else RCC->CFGR |= RCC_CFGR_PPRE1_DIV16;

// Делитель тактовой частоты домена APB2, APB2 Prescaler
if (APB2_DIV == 1) RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
else if (APB2_DIV == 2) RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
else if (APB2_DIV == 4) RCC->CFGR |= RCC_CFGR_PPRE2_DIV4;
else if (APB2_DIV == 8) RCC->CFGR |= RCC_CFGR_PPRE2_DIV8;
else RCC->CFGR |= RCC_CFGR_PPRE2_DIV16;


// В режиме отладки вывожу частоту наружу (вывод MCO PIOA8)
if (DEBUG)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// Для теста. Вывожу SYSCLK на MCO_PIN
PIN_CONFIG(MCO_PIN);
// Вывод частоты на порт
RCC->CFGR &= ~RCC_CFGR_MCO;
RCC->CFGR |= RCC_CFGR_MCO_SYSCLK;
}

return Result;
}


В зависимости от значения макроса выполняется ветка if-else либо
Код
// Если задано тактирование от внешнего кварца HSE (см. <GlobalParams.h>  HSE_CLOCK_SOURCE != 0)
if (HSE_CLOCK_SOURCE != 0)
{
   ....
}
    {

Либо
Код
// Если задано тактирование от внутреннего RC-генератора (см. <GlobalParams.h>  HSE_CLOCK_SOURCE == 0)
else
{
   ...
{


При подаче питания запускается загрузчик и инициализирует тактирование от HSI. Проблем нет.
Загрузчик передаёт управление основной программе и настраивает тактирование от HSE. Проблем нет.
Основное приложение выполняет системный сброс через регистр SCB->AIRCR и ПО опять уходит в загрузчик. HSI не запускается. Причём выяснил, что виснет на строке while (!(RCC->CR & RCC_CR_PLLRDY)); Т.е. PLL1 не запускается. И именно после системного сброса.
Если и загрузчик и основное ПО скомпилированы под работу с внешним HSE - всё работает. Видимо что-то не учёл при выполнении инициализации после системного сброса.
Понимаю что код большой и читать его нет никакого желания (пятница, вечер). Но буду несказанно благодарен человеку, который подтолкнёт на путь истинный. Переходить на HSE в загрузчике пока не хочу по ряду причин

Пятница...вечер. Проблема оказалась описана в Errata, именно мой случай
Цитата
PLL not locking when sourced by HSI/2 after reset if it was
previously sourced by HSE with predivider >1 or PLL2
Description
The limitation occurs when the sequence below is followed:
● PLL source: HSI/2, SYSCLK source: PLL
● PLL source: HSE with predivider >1 or PLL2, SYSCLK source: PLL
● system reset
● PLL source: HSI/2, SYSCLK source: PLL
The PLL cannot be locked when sourced by HSI/2 after applying system reset if it was previously sourced by HSE with predivider >1 or by PLL2.
Workaround
Enable the HSE oscillator and let the PLL lock on it before switching the PLL source to HSI/2.

Т.е. предлагают при запуске тактирования от PLL с источником HSI/2 спрева включить HSE(только как быть если его нет в системе?) подать на PLL сигнал с HSE, дождаться пока он залочится, а уже потом подавать HSI/2 на PLL. Костыль однако.
Пробовал переводить тактирование перед системным сбросом c SHE PLL на HSI (в обход PLL), при переходе к основной программе переключать HSI PLL напрямую на HSI (в обход PLL). Не помогло. Работает только костыль из Errata. Ну да ладно. Может кому ещё пригодится sm.gif
Go to the top of the page
 
+Quote Post

Сообщений в этой теме


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 5th July 2025 - 15:21
Рейтинг@Mail.ru


Страница сгенерированна за 0.01373 секунд с 7
ELECTRONIX ©2004-2016