Sari la conținut
ELFORUM - Forumul electronistilor

Un modest tutorial STM32F103C8T6 (Bluepill)


Postări Recomandate

  • 2 săptămâni mai târziu...
  • Răspunsuri 39
  • Creat
  • Ultimul Răspuns

Top autori în acest subiect

Top autori în acest subiect

Imagini postate

Salut din nou!
Astazi este ultima postare pe acest subiect pentru 2017 si are ca subiect utilizarea unui timer in regim de intreruperi.
Pentru asta urmati primii doi pasi din postarea#1 si faceti selectia asa cum este prezentata in figura de mai jos (foto blink_settings):

post-194122-0-32352900-1513352825_thumb.png

Odata terminate selectiile de mai sus, alegem tab-ul Clock settings (foto blink_clock_settings) si setam valoarea oscilatorului extern HSE la 8MHz, si HCLK la 72MHz, asa cum este prezentat in fotografia de mai jos

post-194122-0-65576000-1513352917_thumb.png

In continuare vom seta TIM1 ca in figura de mai jos (foto blink_TIM1_settings):

post-194122-0-84664600-1513353036_thumb.png

si activam in tab-ul NVIC (Nested Vectored Interrupt Controller) TIM1 update interrupt, ca mai jos:

post-194122-0-60120500-1513353205_thumb.png

Salvam proiectul intr-un folder nou aflat in folderul "Workspace" si generam proiectul pe care, ulterior, il vom deschide selectand butonul 'Open project".

In acest moment avem un fisier "main" care contine aproape toate setarile care sa ne permita sa aprindem si sa stingem un led cu o anumita perioada.

Timerul TIM1 este setat pentru functionare in regim free-runing, astfel ca, dupa activare va incepe sa numere cu o frecventa data de propriul ceas (F_clk/Prescaler), iar cand ajunge la valoarea inscrisa in Period are loc depasirea capacitatii sale de numarare (overflow) si va genera un eveniment de tip Update event (o intrerupere) si procesul de numarare se reia de la zero.
Si totusi trebuie sa ne aducem contributia, astfel ca vom include urmatoarele linii de comanda in fisierul "main":

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();

  /* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim1);//--> Comanda porneste TIM1 in regim free-runing si permite generarea de intreruperi
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

si aceasta rutina, care trateaza intreruperea :

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (__HAL_TIM_GET_FLAG(&htim1,TIM_FLAG_UPDATE) != RESET) {
		__HAL_TIM_CLEAR_IT(&htim1,TIM_IT_UPDATE);
	}
	HAL_GPIO_TogglePin(MyLED_GPIO_Port, MyLED_Pin);
}

Dupa compilare si incarcarea fisierului hex in uC ledul conectat la pinul PC13 va pulsa cu o frecventa de aproximativ 5 Hz (frecventa cu care este accesata rutina HAL_TIM_PeriodElapsedCallback este de aprox. 10Hz), conform formulei:

post-194122-0-70015500-1513353848_thumb.png

Promit sa nu va mai plicitisesc anul asta cu asa ceva!

 

 

main.txt

Link spre comentariu
  • 2 săptămâni mai târziu...

Felicitari pentru initiativa, imi place ca lucrurile pe forum mai evolueaza de la pic/atmel si pe chestii mai profi :)

 

Am parasit si eu de vreo 3 ani familile pe 8 biti, si am trecut brusc la 32biti pe un stm32f407 ( discovery f4board), prima saptamana a fost crunta cu acomodarea :)

 

De atunci doar cu stm32f103 si stm32f4xx lucrez.

 

Personal incerc sa stau departe de librariile HAL, inca le folosesc pe cele "non-hall", se gasesc multe informatii si tutoriale cu ele, dar tine de gustul fiecaruia

 

Ai grija la timere, clock-ul timerului este dat de AHPB_clock / prescaler, la timere acel AHPB dupa derivare din HCLK este dublat.

 

Pe HAL nu stiu daca poti folosi urmatoarele linii de cod

 

RCC_ClocksTypeDef clocks;
RCC_GetClocksFreq(&clocks);
Cu astea in debuger poti sa vezi valorile reale ale clock-urilor, pe mine m-a scos de cateva ori din belele
Editat de Galagie
Link spre comentariu

LA MULTI ANI!

Multumesc pentru aprecieri.

Este optiunea fiecaruia de a alege SPL sau HAL.

Eu am ales HAL pentru simplul motiv ca ST nu mai ofera suport tehnic pentru SPL.

Cum scheletul proiectului este generat de CubeMX, grijile referitoare la semnalul de ceas aferent bunei functionari a timerelelor dispar, intrucat inca de la inceput utilizatorul va sti ce frecventa va avea ceasul pentru un anumit timer (are imaginea clara in fereastra de "Clock settings").

Link spre comentariu
  • 3 săptămâni mai târziu...

In sfarsit a aparut versiunea 9.0 a Atollic True Studio: https://atollic.com/resources/download/windows/windows-archive/?submissionGuid=e68f10b0-8cff-4387-9a45-b45da67379f0

Un plus este ca noua versiune este Pro si nu Lite, asta inseamnand ca toate facilitatile sunt deblocate si este free.

Un minus (minor pentru utilizatori ca mine) este ca functioneaza doar pentru microcontrollere-le din seria STM32 ( nu ca pana acum pentru toate microcontrollerele ARM).

Vom vedea in viitor cum se comporta.

Link spre comentariu
  • 2 săptămâni mai târziu...

Daca ar fi sa ma iau dupa numarul de vizualizari al acestui topic ajung la concluzia ca totusi ceva interes prezinta. Si totusi... cu cateva exceptii, nimeni nu prea este interesat de acest tip de microcontrollere (mult mai "interesant" este razboiul orgoliilor din alte sectiuni!).

Sunt constient de faptul ca modul de prezentare al tutorialului poate lasa putin de dorit: prea putina vorbarie, fara prea multe explicatii de ce asa si nu altfel! Am considerat insa ca imaginile fac mai mult decat o mie de cuvinte (jur ca nu este intentia mea sa plagiez pe cineva pentru aceasta expresie). In plus, postarea proiectului in integralitate (cu mici exceptii) poate servi ca baza de plecare pentru studiu mai aprofundat.

OK, sa depasim momentul!

Astazi va prezint o facilitate interesanta a timerelor de uz general si a celor avansate ce fac parte din structura STM32, mai precis de functia One Pulse Mode (OPM).

Aceasta functie este un mix intre functiile de "Input Capture" si "Output Compare" oferite de categoria mentionata de timere si permite acestora sa inceapa functionarea ca raspuns la un stimul extern, cu o anumita intarziere si pentru o anumita durata.

OPM este proiectata pentru a folosi exclusiv canalele "Channel 1" si "Channel 2" ale timerului, acestea putand fi selectate folosind functia:

HAL_TIM_OnePulse_ConfigChannel(TIM_HandleTypeDef *htim, TIM_OnePulse_InitTypeDef* sConfig, uint32_t OutputChannel, uint32_t InputChannel);

N.B: Timerul trebuie sa functioneze in regim "Slave mode", iar sursa de trigger-are sa fie TI1FP1 (aferenta Channel 1) sau TI2FP2 (aferenta Channel 2) depinde de canalul selectat ca intrare , in caz contrar functia OPM nu lucreaza!

Calculul intarzierii la pornire este dat de formula:

 

post-194122-0-19074000-1517569584_thumb.png

Iar calculul duratei impulsului este dat de formula:

post-194122-0-82862700-1517570431_thumb.png

Pentru setari, urmati primii doi pasi din postarea#1 si faceti selectia asa cum este prezentata in figura de mai jos (foto settings):

 

post-194122-0-39966200-1517570620_thumb.png

Selectati frecventa ceasului intern, ca mai jos (foto: clock_settings):

 

post-194122-0-75955600-1517570663_thumb.png

Pasul urmator este configurarea timerului (eu am ales TIM1), ca in fotografia de mai jos:

 

post-194122-0-05435400-1517570741_thumb.png

Si binenteles configurarea NVIC (am vorbit despre asta in postrea precedenta):

 

post-194122-0-02516800-1517570799_thumb.png

Generam proiectul si obtinem astfel proiectul cu setarile de baza (in stare de reset a microcontroller-ului).

Ne aducem si noi contributia cu ceva, inserand cele doua comenzi de mai jos, in pozitia corespunzatoare:

int main(void)
{

   /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();

/*Aici sunt introduse cele doua comenzi de pornire a timerului */

  /* USER CODE BEGIN 2 */
	HAL_TIM_Base_Start_IT(&htim1);
	HAL_TIM_OnePulse_Start_IT(&htim1, TIM_CHANNEL_2);
  /* USER CODE END 2 */

 	while (1) {
  
	}

}

Compilam fisierul si il incarcam in microcontroller.

Conectam un buton la portul PA.8 si GND si un LED, printr-o rezistenta de 100 ohm, la portul PA.9 si vizualizam rezultatul muncii: de fiecare data cand apasam butonul dupa o anumita intarzire (in cazul nostru de circa 3 secunde) LED-ul se va aprinde o anumita durata de timp (in acest caz circa 2 secunde) apoi se stinge.

Aveti atasat fisierul main si incerc sa postez si proiectul integral.

main.txt

Link spre comentariu
  • 8 luni mai târziu...

M-am decis sa continui cu acest mic tutorial, incurajat fiind doar de  numarul mare de vizualizari.Cat despre reactii ... cateva, raportate la numarul de vizualizari. 

Sper totusi ca ceea ce prezint aici, asa, in stilul meu mai putin pedagogic, sa foloseasca celor care vor sa invete despre aceste microcontrollere puternice si foarte versatile.

In continuare ramanem cantonati la timere si voi prezenta o modalitate de a genera semnal sinusoidal folosind functia de generator de PWM (STM32F103C8T6 nu dispune de DAC).

Ideea de baza este aceea de a genera pulsuri cu latimea variabila care, prin filtrare, sa duca la obtinerea unui semnal sinusoidal.

Pentru asta apelam la tehnica DDS, conceptul de baza fiind de a calcula faza la fiecare moment de eșantionare.  În general, pentru o frecvență arbitrară de eșantionare Fsample și frecvența dorita Fout, faza trebuie să crească cu R = 2 * pi * ( Fout/Fsample) la fiecare moment de eșantionare.

Pentru setari, urmati primii doi pasi din postarea#1 si faceti selectia asa cum este prezentata in figura de mai jos:

 

1623400495_ConfigurareSTM32F103.thumb.png.ffe168966e911e1c5d8fbd747fc9b40f.png

 

Dupa cum se vede am selectat pentru aceasta aplicatie timerul TIM2 si canalul 1 al acestuia pentru generarea de semnal PWM.

Selectam tab-ul "Clock Configuration" si facem setarile pentru semnalul de ceas, ca mai jos:

 

clock_config.thumb.png.8e96ccd44841b86d4520716267bfbe56.png

 

Pasul urmator ar fi, in mod normal sa configuram Timer-ul 2 pentru functia de generare a semnalului PWM, asa ca selectam tab-ul "Configuration" si procedam in felul urmator:

- Selectam butonul "TIM2" si se deschide fereastra de configurare propriu-zisa. La sectiunea "PWM Generation Channel 1", in dreptul setarii "Mode" alegeti "PWM mode 1" din fereastra defilanta si apasati butonul "Apply" (vezi mai jos)

 

TIM2_config.thumb.png.0ae7870e206d331e147a3a1297697f25.png

 

Selectati tabul "NVIC Settings" si vom bifa in noua fereastra "TIM2 Global Interrupt", asta pentru avem nevoie ca timerul sa lucreze in regim de intreruperi.

 

NVIC_config.thumb.png.daae857d767eb7b60cbd612e25e13930.png

 

In final setam portul de iesire sa functioneze in regim de push-pull si cu o viteza maxima:

 

Port_TIM2_out_config.thumb.png.d98c6cf8e576f419cf3b9b28b8bf1894.png

 

Salvam totul sub un nume convenabil si generam proiectul, care, asa cum am mai spus, va contine doar setarile in stare de reset ale microcontroller-ului.

Acesta este fisierul "main.c", asa cum este generat de CubeMX si completat de mine cu variable si alte cele necesare functionarii proiectului.


/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  ** This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * COPYRIGHT(c) 2018 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_hal.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
#define POW232 4294967296  //valoarea 2^32

uint16_t sinetable[] = { 256, 262, 269, 275, 281, 287, 294, 300, 306, 312, 318,
		324, 330, 336, 342, 348, 354, 360, 365, 371, 377, 382, 388, 393, 398,
		403, 408, 413, 418, 423, 428, 433, 437, 441, 446, 450, 454, 458, 462,
		465, 469, 472, 476, 479, 482, 485, 487, 490, 493, 495, 497, 499, 501,
		503, 504, 506, 507, 508, 509, 510, 511, 511, 512, 512, 512, 512, 512,
		511, 511, 510, 509, 508, 507, 506, 504, 503, 501, 499, 497, 495, 493,
		490, 487, 485, 482, 479, 476, 472, 469, 465, 462, 458, 454, 450, 446,
		441, 437, 433, 428, 423, 418, 413, 408, 403, 398, 393, 388, 382, 377,
		371, 365, 360, 354, 348, 342, 336, 330, 324, 318, 312, 306, 300, 294,
		287, 281, 275, 269, 262, 256, 250, 243, 237, 231, 225, 218, 212, 206,
		200, 194, 188, 182, 176, 170, 164, 158, 152, 147, 141, 135, 130, 124,
		119, 114, 109, 104, 99, 94, 89, 84, 79, 75, 71, 66, 62, 58, 54, 50, 47,
		43, 40, 36, 33, 30, 27, 25, 22, 19, 17, 15, 13, 11, 9, 8, 6, 5, 4, 3, 2,
		1, 1, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 15, 17, 19, 22,
		25, 27, 30, 33, 36, 40, 43, 47, 50, 54, 58, 62, 66, 71, 75, 79, 84, 89,
		94, 99, 104, 109, 114, 119, 124, 130, 135, 141, 147, 152, 158, 164, 170,
		176, 182, 188, 194, 200, 206, 212, 218, 225, 231, 237, 243, 250, };

#define Fsample 100000
#define Fout  1000
uint32_t r = 0;
uint32_t fs = 0;
uint32_t fo = 0;
float temp = 0;
uint16_t pulse_width = 0;
uint32_t phase_accumulator = 0;
uint8_t angle = 0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);                                    
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
                                

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  *
  * @retval None
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  /*Pentru a genera un semnal PWM - cu frecventa Fsample - care, prin filtrare, sa ne furnizeze un semnal sinusoidal
   * cu frecventa Fout, vom folosi formula de calcul: R = (2^32) * Fout/Fsample */
	fs = Fsample;
	fo = Fout;
	temp = (float) (1.0 / (fs / fo));
	r = (uint32_t) (POW232 * temp);
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
	HAL_TIM_Base_Start_IT(&htim2);
	HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1) {

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

	}
  /* USER CODE END 3 */

}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/* TIM2 init function */
static void MX_TIM2_Init(void)
{

  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 0;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  HAL_TIM_MspPostInit(&htim2);

}

/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @param  file: The file name as string.
  * @param  line: The line in file as a number.
  * @retval None
  */
void _Error_Handler(char *file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
	/* User can add his own implementation to report the HAL error return state */
	while (1) {
	}
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
	/* User can add his own implementation to report the file name and line number,
	 tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Mai ramane sa adaugam rutina apelata de timerul TIM2 la fiecare intrerupere, in sectiunea USER CODE BEGIN 4 ... USER CODE END 4, respectiv cea de mai jos

 

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

	if (htim->Instance == TIM2) {
		if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET) { //resetare flag intrerupere
			__HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);
		}
		phase_accumulator += r;

		angle = (uint8_t) ((phase_accumulator >> 24));

		pulse_width = sinetable[angle];

		TIM2->CCR1 = pulse_width; //o varianta mai scurta de a trimite la portul de iesire semnalul PWM
	}
}

Compilati programul si incarcati-l in microcontroller.

 

Prin conectarea unui filtru trece-jos la portul A0 veti obtine o frumoasa sinusoida.

Un exemplu de filtru trece-jos ar fi cel de aici:

 

pwm_out.thumb.png.094ee92d1cb6194daad292093f221743.png

 

Cam atat pentru azi.

Si proiectul complet.

sin_wave_f103.zip

 

L.E.: Am uitat un mic "detaliu"

In cadrul rutinei de setare a TIM2, trebuie introdus ce este mai jos:

 htim2.Init.Period = (SystemCoreClock/fs)-1;

in loc de :

htim2.Init.Period = 0;

In lipsa acestui amanunt nu veti obtine niciun semnal.

Editat de nico_2010
Link spre comentariu

Multumim pentru efortul depus.

ARM Cortex este un subiect mai avansat, aceasta justifica tacerea de pe topic :) Cand te uiti la aproape 1200 de pagini de functii  in manualul HAL ... 

https://www.st.com/content/ccc/resource/technical/document/user_manual/72/52/cc/53/05/e3/4c/98/DM00154093.pdf/files/DM00154093.pdf/jcr:content/translations/en.DM00154093.pdf

 

VisualGDB au unele tutoriale STM32 interesante: https://visualgdb.com/w/tutorials/category/tutorials/embedded/stm32_periph/

 

Editat de mars01
Link spre comentariu

Sunt de acord cu tine ca volumul de informatie este mare si descurajant, insa nu cred ca trebuie sa inveti pe dinafara tot manualul.

Cred ca accesarea acelor informatii care sa te ajute atunci cand scrii un program (de exemplu, rutina de tratare a intreruperii pentru un timer sau ADC) te scuteste de multe migrene. Ar fi suficient sa dai click pe fisierul care contine functiile pentru gestionarea perifericului respectiv (ex. stm32f1xx_hal_adc.c, stm32f1xx_hal_tim.c) si in fereastra din dreapta a IDE Atollic vei avea toate functiile continute.

 

Link spre comentariu
  • 2 săptămâni mai târziu...

Pretul in FLASH ocupat care se plateste folosind driverele HAL este destul de mare (spatiu FLASH ocupat dublu). Desi folosind HAL consuma ceva mai putin RAM.

 

Un program care face blink cu un delay non-blocant folosind un STM32F0xx:

 

Folosind HAL:

#include "stm32f0xx_hal.h"
#define LD2_Pin GPIO_PIN_5
#define LD2_GPIO_Port GPIOA
  
volatile uint32_t time_now = 0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);

void hw_init(){
	/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
	HAL_Init();

	/* Configure the system clock */
	SystemClock_Config();

	/* Initialize all configured peripherals */
	MX_GPIO_Init();
}

int main(void){
	hw_init();
	time_now = HAL_GetTick();

	while(1){
		static uint8_t st = 0;

		if(!st && HAL_GetTick() >= (time_now + 500)){
			st = 1;
			HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, st);
			time_now= HAL_GetTick();
		}

		if(st && HAL_GetTick() >= (time_now + 50)){
			st = 0;
			HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, st);
			time_now= HAL_GetTick();
		}
	}
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{

	RCC_OscInitTypeDef RCC_OscInitStruct;
	RCC_ClkInitTypeDef RCC_ClkInitStruct;
	RCC_PeriphCLKInitTypeDef PeriphClkInit;

    /**Initializes the CPU, AHB and APB busses clocks  */
	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48;
	RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
	HAL_RCC_OscConfig(&RCC_OscInitStruct);

    /**Initializes the CPU, AHB and APB busses clocks */
	RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
							  |RCC_CLOCKTYPE_PCLK1;
	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI48;
	RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
	RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

	HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);

	PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
	PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;

	HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);

    /**Configure the Systick interrupt time */
	HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick  */
	HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

	/* SysTick_IRQn interrupt configuration */
	HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/** Configure pins as 
	* Analog
	* Input
	* Output
	* EVENT_OUT
	* EXTI
*/
static void MX_GPIO_Init(void){

	GPIO_InitTypeDef GPIO_InitStruct;

	/* GPIO Ports Clock Enable */
	__HAL_RCC_GPIOA_CLK_ENABLE();

	/*Configure GPIO pin Output Level */
	HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

	/*Configure GPIO pin : LD2_Pin */
	GPIO_InitStruct.Pin = LD2_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
}

 

 text       data        bss        dec   

4022         52        268       4342

FLASH ocupat = 4022 + 52 = 4074 bytes

RAM ocupat = 268 + 52 = 320 bytes ocupat

 

Folosind SPL (Standard Peripheral Lib):

#include <stdlib.h>
#include "stm32f0xx.h"
#include "cortexm/ExceptionHandlers.h"


#define BLINK_ON_TICKS  250
#define BLINK_OFF_TICKS 100

volatile uint32_t counter_isr = 0;
uint32_t time_now;

void hw_config(){
	// CLOCK CONFIG
	// start internal 48MHz oscillator
	RCC_HSI48Cmd(ENABLE);
	// wait for HSI48 to start
	while (RCC_GetFlagStatus(RCC_FLAG_HSI48RDY) == RESET);

	// set HSI48 as SYSCLK
	RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI48);

	// set HCLK as 48MHz
	RCC_HCLKConfig(RCC_SYSCLK_Div1);

	// set PCLK as 48MHz
	RCC_PCLKConfig(RCC_HCLK_Div1);

	// SYSTICK CONFIG
	SystemCoreClock = 48000000;
	SysTick_Config (SystemCoreClock / 1000);
	NVIC_SetPriority (SysTick_IRQn, 0);

	// PORT CONFIG
	// Enable GPIO Peripheral clock
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;

	// Configure pin in output push/pull mode
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}


void SysTick_Handler(void){
	counter_isr++;
}

uint32_t GetTick(){
	volatile uint32_t time;
	time = counter_isr;
	return time;
}

int main(void){
	uint8_t led_state = 0;
	hw_config();

	time_now = GetTick();
	while (1){
		if (GetTick() >= (time_now + BLINK_ON_TICKS) && !led_state){
			GPIO_WriteBit(GPIOA, GPIO_Pin_5, ENABLE);
			time_now = GetTick();
			led_state = 1;
		}

		if (GetTick() >= (time_now + BLINK_OFF_TICKS) && led_state){
			GPIO_WriteBit(GPIOA, GPIO_Pin_5, DISABLE);
			time_now= GetTick();
			led_state = 0;
		}

	}
}

 

text       data        bss        dec 
1896         68        268      2232

 

FLASH ocupat = 1896 + 68 = 1964 bytes

RAM ocupat = 268 + 68 = 336 bytes ocupat

Editat de mars01
Link spre comentariu

@mars01 ai perfecta dreptate si recunosc ca de acest aspect m-am lovit si eu de curand cand am inceput sa lucrez la softul pentru o punte RCL (la care partea de hard este cam gata).

Momentan, folosesc o placa de dezvoltare Nucleo F303RE si, cel putin in stadiul asta, deja am consumat circa 20k Flash si 2k RAM si nu vreau inca sa stiu care va fi finalul.

Este foarte posibil asa fiu nevoit sa trec pe SPL sau, poate si mai convenabil, direct la programarea registrilor - acolo unde este cazul - pentru a ma incadra in posibilitatile unui STM32F100C6T6.

Vom vedea.

L.E.: Care a fost nivelul de optimizare in cele doua cazuri?

Editat de nico_2010
Link spre comentariu

Alătură-te conversației

Poți posta acum și să te înregistrezi mai târziu. Dacă ai un cont, autentifică-te acum pentru a posta cu contul tău.
Notă: Postarea ta va necesita aprobare moderator înainte de a fi vizibilă.

Vizitator
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Răspunde la acest subiect...

×   Alipit ca text avansat.   Restituie formatare

  Doar 75 emoji sunt permise.

×   Linkul tău a fost încorporat automat.   Afișează ca link în schimb

×   Conținutul tău precedent a fost resetat.   Curăță editor

×   Nu poți lipi imagini direct. Încarcă sau inserează imagini din URL.




×
×
  • Creează nouă...

Informații Importante

Am plasat cookie-uri pe dispozitivul tău pentru a îmbunătății navigarea pe acest site. Poți modifica setările cookie, altfel considerăm că ești de acord să continui.Termeni de Utilizare si Ghidări