Простой старт STM32 Тактирование и задержка

Одним из важных моментов для ARM является настройка тактирования, как ядра так и периферии. Также для меня до сих пор является загадкой где же функции задержки т.к. во всех примерах используется тупой цикл.Это мы и попробуем прояснить как обычно на примере Discovery STM32 в CooCox IDE www.coocox.org/Tools/CoIDE-1.2.5.exe (правда с возникновением новых вопросов....) Если что не так надеюсь камрады поправят.
Просмотрев несколько примеров от CooCox — самых что ни есть простых я не обнаружил ответа на прямой вопрос — как же конфигурируется тактирование… нашел только в более сложных примерах про SPI, причем оказалось что по умолчанию почемуто все стартует совершенно правильно т.е. где то конфигурируется — причем с PLL (загадка №1 — разгадана спасибо Vga см. файл system_stm32f10x.c функция SystemInit () в папке cmsis_boot в томже файле устанавливаются по умолчанию параметры работы с флеш спасибо DI Halt)
Посмотреть можно просто через функцию — void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks)
после ее вызова она заполняет структуру -RCC_Clocks.
Приведенный здесь код является слегка модифицированным из предыдущей статьи т.е. просто моргает светодиодиками на Дискавери.
На всякий случай привожу структуру тактирования.

Одной из применений функции RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks) будет функция задержки.
Ну теперь попорядку. Функция void RCC_Configuration(void) является оболочкой для процедуры настройки тактирования. Сначала производится сброс всего RCC_DeInit(), потом включение внешнего генератора — RCC_HSEConfig(RCC_HSE_ON), затем ждем стабилизации генератора HSEStartUpStatus = RCC_WaitForHSEStartUp() и только после этого при успешном старте генератора происходит дальнейшая настройка — если нет то включается внутренний генератор HSI.
Считаем что кварц завелся и мы конфигурируем ядро и иже сними RCC_HCLKConfig(RCC_SYSCLK_Div1), периферийная шина1 (медленная) RCC_PCLK1Config(RCC_HCLK_Div1), шина2 (Быстрая- в том числе GPIO ) RCC_PCLK2Config(RCC_HCLK_Div1) ну и на всякий случай АЦП (тоже тактируется отдельно) — RCC_ADCCLKConfig(RCC_PCLK2_Div2)- Значения и названия параметров можно посмотреть в хелпе — но они чисто по смыслу понятны например ....Div1 — это делитель=1, Div2=2.
Далее настраиваем PLL — функция RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_3) настраивает источник -RCC_PLLSource_PREDIV1 и коэфицент умножения RCC_PLLMul_3 — =3 и т.к. частота кварца у нас 8МГц то получаем 24МГц на PLL и надо немножко подождать до стабилизации — while (RCC_GetSYSCLKSource() != 0x08) {}. Да и не забыть подключить используемую периферию например GPIOC — RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE).

Ну загадка отсутствия функции таймера… для меня досих пор неразгадана поэтому раз мы знаем частоту то вроде как одна итерация цикла это оказалось не один цикл ядра а примерно 10 (как подсказал neiver) и собственно при знании этого параметра мы можем узнать сколько нам надо циклов на единицу времени — поэтому родилась такакя функция- Delay_ms(uint32_t ms) единственная Загадка №2 — разрешилась заключается в том что делить приходиться на 10000… а не на 1000 (выяснено эмпирическим путем) — nCount=(RCC_Clocks.HCLK_Frequency/10000)*ms.



Если кварц выдернуть то все стартует с частотой 8МГц — это частота HSI
Ну и код:



#include<stm32f10x_rcc.h>
#include<stm32f10x_gpio.h>

#include "stm32f10x.h"
#include "stm32f10x_conf.h"

void Delay_ms(uint32_t ms);
void RCC_Configuration(void);
ErrorStatus HSEStartUpStatus;

GPIO_InitTypeDef GPIO_InitStructure;

RCC_ClocksTypeDef RCC_Clocks;

volatile int main(void)

{
	RCC_GetClocksFreq (&RCC_Clocks);

	RCC_Configuration();

	RCC_GetClocksFreq (&RCC_Clocks);

	// init for GPIO (LED)
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8 | GPIO_Pin_9 ;       // two LED (guess on what pin!!)
        GPIO_Init(GPIOC, &GPIO_InitStructure);



    while(1)
    {
        GPIO_WriteBit(GPIOC,GPIO_Pin_8,Bit_RESET);
                GPIO_WriteBit(GPIOC,GPIO_Pin_9,Bit_SET);
                Delay_ms(1000);
                GPIO_WriteBit(GPIOC,GPIO_Pin_9,Bit_RESET);
                GPIO_WriteBit(GPIOC,GPIO_Pin_8,Bit_SET);
                Delay_ms(1000);
    }

}

//-------
void Delay_ms(uint32_t ms)
{
	volatile uint32_t nCount;
	RCC_ClocksTypeDef RCC_Clocks;
    RCC_GetClocksFreq (&RCC_Clocks);

	nCount=(RCC_Clocks.HCLK_Frequency/10000)*ms;
	for (; nCount!=0; nCount--);
}
//------------
void RCC_Configuration(void)
{
    /*RCC system reset(for debug purpose) */
    RCC_DeInit();

    /* Enable HSE */
    RCC_HSEConfig(RCC_HSE_ON);

    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();

    if (HSEStartUpStatus == SUCCESS)
    {
        /* HCLK = SYSCLK */
        RCC_HCLKConfig(RCC_SYSCLK_Div1);

        /* PCLK2 = HCLK*/
        RCC_PCLK2Config(RCC_HCLK_Div1);

        /* PCLK1 = HCLK*/
        RCC_PCLK1Config(RCC_HCLK_Div1);

        //ADC CLK
        RCC_ADCCLKConfig(RCC_PCLK2_Div2);

        /* PLLCLK = 8MHz * 3 = 24 MHz */
        RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_3);



        /* Enable PLL */
        RCC_PLLCmd(ENABLE);

        /* Wait till PLL is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {}

        /* Select PLL as system clock source */
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

        /* Wait till PLL is used as system clock source */
        while (RCC_GetSYSCLKSource() != 0x08) {}
    }

    /*Then need to enable peripheral clocks ----------------------------------------------*/
}



  • 0
  • 12 августа 2011, 17:23
  • GYUR22

Комментарии (16)

RSS свернуть / развернуть
нашел только в более сложных примерах про SPI, причем оказалось что по умолчанию почемуто все стартует совершенно правильно т.е. где то конфигурируется — причем с PLL (загадка №1)
Обычно это в стартап-коде происходит. Обычно в функции SystemInit.
0
  • avatar
  • Vga
  • 12 августа 2011, 17:33
А где она там и как узнать какой по дефолту конфиг?
0
Где-то в недрах CMSIS, по идее.
0
нашел system_stm32f10x.c
т.е по умолчанию процы стартуют по максимуму?
0
Зависит от систем инит конкретной либы. По дефолту там вообще ничего нет. У Кокосовских примеров там вроде что то прописано. Но не везде. В кейл стартап ваще пустой.
0
почемуто делить приходиться на 10000… а не на 1000
for (; nCount!=0; nCount--);

Одна итерация этого цикла как раз около 10 тактов ядра, однако никаких гарантий на это нет.
0
Я вот такой функцией для програмных задержек пользуюсь в GCC:
inline void DelayLoop(uint32_t delayLoops)
{
	__asm__ __volatile__
	(
		"1: \n"
		" CBZ %[delayLoops], 2f \n"
		" SUB %[delayLoops], %[delayLoops], #1 \n"
		" B 1b \n"
		"2: \n"
		: [delayLoops] "+r"(delayLoops)
	);
}

Гарантированно 3 такта на итерацию.
0
спасибо учтем.
Но вообще странно — казалось для ARM асемблер не нужен…
0
ОЛОЛОЛО! Ассемблер рано или поздно нужен будет в любой системе :)
0
На чём эксперименты проходили?
просто на stm32f4 discovery приходится брять не HCLK_Frequency, а HCLK_Frequency для того чтобы добиться приемлимых результатов.
0
Дабы не создавать вопрос на форуме. Подскажите по тактированию АЦП:
-АЦП может тактироваться от HSI14 или от PCLK,
-при старте контроллера выполняется функция SystemInit, в которой регисты, определяющие тактирование АЦП сбрасываются в 0
/* Reset USARTSW[1:0], I2CSW, CECSW and ADCSW bits */
  RCC->CFGR3 &= (uint32_t)0xFFFFFEAC;

  /* Reset HSI14 bit */
  RCC->CR2 &= (uint32_t)0xFFFFFFFE;

Причём, в регистре CFGR3 при сбросе осуществляется выбор HSI14 (т.к. соотв. бит =0), а в CR2 при сбросе HSI14 выключается.
Я посмотрел пример применения АЦП, но в нём нигде эти регистры не меняются, т.е. после сброса АЦП должен тактироваться от HSI14, который при этом выключен. Однако пример работает!
Вопрос 1: Как такое может быть?
Вопрос 2: Как понял, в примере (там только main.c, который я скопировал, а не весь проект) подразумевается, что программист сам должен настраивать тактирование каждого модуля?
0
Кто-нибудь с новым HAL-ом разбирался? STM депрекэйтнули STDLib.
Я начал, вроде не сложно.
0
И что из себя новый HAL представляет?
Алсо, не напомнишь ссылки на него и на STDLib?
+1
Вот ссылка на HAL для серии L1:
www.st.com/web/catalog/tools/FM147/CL1794/SC961/SS1743/LN1897/PF260821
Можно найти все версии в поиске на сайте st.com по слову «cube»
HAL — это замена SPL. ST рекомендует его для новых разработок. Я только начал разбираться. Вчера скомпилировал пример оттуда для GPIO с помощью gcc. Вот выдержка из доки:
The STM32Cube HAL Layer is the replacement of the Standard Peripheral Library.
The HAL APIs offer a higher abstraction level compared to the standard peripheral APIs. HAL focuses on peripheral common functionalities rather than hardware. The higher abstraction level allows to define a set of user friendly APIs that can be easily ported from one product to another.
Customers currently using Standard Peripheral Libraries will be helped through Migration guides. Existing Standard Peripheral Libraries will be supported, but not recommended for new designs.
В архиве этого Cube есть и SPL. Но в примерах он не использиуется. CMSYS используется по-прежнему.
0
Там ничего сложного нет. Не понравилось что обработка прерываний делается callback функциями (хотя и не обязательно, но рекомендуют так) и связь ДМА с периферией делается линковкой. Зато настройка тактирования и PLL сильно упростилась.
Вот настройка тактирования:
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

Вот обработка прерывания по методу HAL (Buffer1Complect и Buffer2Complect задаются при инициализации ДМА):
static void Buffer1Complect(DMA_HandleTypeDef *DmaHandle)
{
		request_buf=1;
}
static void Buffer2Complect(DMA_HandleTypeDef *DmaHandle)
{
		request_buf=0xFF;
}
void DMA1_Stream5_IRQHandler(void)
{
  HAL_DMA_IRQHandler(DacHandle.DMA_Handle1);
}

А это собственно связь ДМА с ЦАП:
// Link DAC-DMA
    __HAL_LINKDMA(&DacHandle, DMA_Handle1, DmaHandle);
    // Multi buffer
    HAL_DMAEx_MultiBufferStart_IT(&DmaHandle,(uint32_t)&buffer1, (uint32_t) &DAC->DHR8RD,(uint32_t)&buffer2, BUF_SIZE*2);

При этом некоторые функции вызова задаются руками, а некоторые уже заданы.
Вот обработка таймера (HAL_TIM_PeriodElapsedCallback системное):
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_14);
}
void TIM2_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&Tim2Handle);
}
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.