Developers Club geek daily blog

1 year, 4 months ago

Introduction.

In the previous article we repeated the general structure of the timer and in details considered manual method of adjusting PWM of the channel with use of CMSIS. But much do not like "to dig in registers" and they prefer essentially other abstraction layer allowing as it seems to them, to simplify a task. I will try to show in this article to you all pluses and minuses of this approach.

Change in material supply.

In all previous articles I described all knowledge of any task in one consistently created text, trying not to miss all subtleties and parts, receiving at the same time rather volume, but settling any questions, article. As a result, opinions on my articles were shared. It was pleasant to someone that in articles there are no sendings to literature and all necessary information for understanding of article is in article or in her predecessors. And it was on the contrary not interesting to someone to read about "elementary things" (such as language syntax, standard organization of blocks, etc.) and people closed article of not read. As I do not like to send people to literature, but at the same time I do not want that something in article was unclear, material will be stated as follows now: the body text — the text for the people understanding about what read and having some experience on this subject. For those who do not know something or not up to the end understand — as spoilers with marks "Explanations to...." — all will be brought together information, necessary for understanding of article.

Task.

Our task to solve the same problem that we solved in the previous article, but with use only of SPL opportunities. On a result we will be able to tell what approach is more evident, fast weighs also less. As a result — we will create as much functions how many was in the previous implementation with the same names except that we will add "SPL" that it was possible to distinguish them and to compare influence of each function on weight and performance of a code (Replacing function of manual initialization with function with automatic initialization by means of SPL).

Setup of input/output ports means of SPL (PORT).

I suggest to begin with the simplest. From input/output ports for manually the managed LED. Earlier this function was called initPinPortCForLed. Now there will be initPinPortCForLedSPL. Names of the subsequent functions will have the same principle of naming. As we remember in order that the port was started and we could light a LED — it is necessary:
  1. To give a clocking signal on the port.
  2. To initialize port.
  3. To expose value in the register RXTX.
So was when we worked with CMSIS directly. With SPL all is a little differently. For setup of any peripheral module it is necessary to fill structure. Sometimes — not one. And then to transfer her to function which will configure everything.
The explanation to told.
It is possible to draw an analogy to construction of the house: you do the drawing on all requirement, and then give him to people who build the house. Does not concern you as the house will be built. You mean that in accuracy — as on your drawing. Here "drawing" is the structure configured by you. And "the people building the house" — the SPL function. For each block of the periphery there is the structure. To learn what is required structure, it is possible having glanced in the file in the spl folder (in a project tree) with the name Mdr32f9qx_imya_periferii.
For a start we need to give a clocking signal on the port. For this purpose we need to address the MDR32F9Qx_rst_clk.h file. In it, right at the end, there are functions which SPL can provide us. From all variety of functions, we are interested only RST_CLK_PCLKcmd. With its help we can give a clocking signal on any block of the periphery.
void RST_CLK_PCLKcmd(uint32_t RST_CLK_PCLK, FunctionalState NewState);
Function has two parameters:
  • RST_CLK_PCLK — periphery block name on which it is necessary to give or from which it is necessary to remove a clock signal (a clocking signal). The file can find possible names search in the same.h, having gathered RST_CLK_PCLK as required.
    Possible values of the RST_CLK_PCLK parameter
    #ifdef USE_MDR1986VE9x /* For cortex M3 */
    
    	#define RST_CLK_PCLK_CAN1           PCLK_BIT(MDR_CAN1_BASE)
    	#define RST_CLK_PCLK_CAN2           PCLK_BIT(MDR_CAN2_BASE)
    	#define RST_CLK_PCLK_USB            PCLK_BIT(MDR_USB_BASE)
    	#define RST_CLK_PCLK_EEPROM         PCLK_BIT(MDR_EEPROM_BASE)
    	#define RST_CLK_PCLK_RST_CLK        PCLK_BIT(MDR_RST_CLK_BASE)
    	#define RST_CLK_PCLK_DMA            PCLK_BIT(MDR_DMA_BASE)
    	#define RST_CLK_PCLK_UART1          PCLK_BIT(MDR_UART1_BASE)
    	#define RST_CLK_PCLK_UART2          PCLK_BIT(MDR_UART2_BASE)
    	#define RST_CLK_PCLK_SSP1           PCLK_BIT(MDR_SSP1_BASE)
    	#define RST_CLK_PCLK_09             PCLK_BIT(0x40048000)
    	#define RST_CLK_PCLK_I2C            PCLK_BIT(MDR_I2C_BASE)
    	#define RST_CLK_PCLK_POWER          PCLK_BIT(MDR_POWER_BASE)
    	#define RST_CLK_PCLK_WWDG           PCLK_BIT(MDR_WWDG_BASE)
    	#define RST_CLK_PCLK_IWDG           PCLK_BIT(MDR_IWDG_BASE)
    	#define RST_CLK_PCLK_TIMER1         PCLK_BIT(MDR_TIMER1_BASE)
    	#define RST_CLK_PCLK_TIMER2         PCLK_BIT(MDR_TIMER2_BASE)
    	#define RST_CLK_PCLK_TIMER3         PCLK_BIT(MDR_TIMER3_BASE)
    	#define RST_CLK_PCLK_ADC            PCLK_BIT(MDR_ADC_BASE)
    	#define RST_CLK_PCLK_DAC            PCLK_BIT(MDR_DAC_BASE)
    	#define RST_CLK_PCLK_COMP           PCLK_BIT(MDR_COMP_BASE)
    	#define RST_CLK_PCLK_SSP2           PCLK_BIT(MDR_SSP2_BASE)
    	#define RST_CLK_PCLK_PORTA          PCLK_BIT(MDR_PORTA_BASE)
    	#define RST_CLK_PCLK_PORTB          PCLK_BIT(MDR_PORTB_BASE)
    	#define RST_CLK_PCLK_PORTC          PCLK_BIT(MDR_PORTC_BASE)
    	#define RST_CLK_PCLK_PORTD          PCLK_BIT(MDR_PORTD_BASE)
    	#define RST_CLK_PCLK_PORTE          PCLK_BIT(MDR_PORTE_BASE)
    	#define RST_CLK_PCLK_26             PCLK_BIT(0x400D0000)
    	#define RST_CLK_PCLK_BKP            PCLK_BIT(MDR_BKP_BASE)
    	#define RST_CLK_PCLK_28             PCLK_BIT(0x400E0000)
    	#define RST_CLK_PCLK_PORTF          PCLK_BIT(MDR_PORTF_BASE)
    	#define RST_CLK_PCLK_EBC            PCLK_BIT(MDR_EBC_BASE)
    	#define RST_CLK_PCLK_31             PCLK_BIT(0x400F8000)
    
    	#define IS_RST_CLK_PCLK(PCLK)       ((((PCLK) &RST_CLK_PCLK_09) == 0x00) &&\
    										 (((PCLK) &RST_CLK_PCLK_26) == 0x00) &&\
    										 (((PCLK) &RST_CLK_PCLK_28) == 0x00) &&\
    										 (((PCLK) &RST_CLK_PCLK_31) == 0x00))
    #endif // #ifdef USE_MDR1986VE9x /* For cortex M3 */
    I ask to pay attention that for each series of microcontrollers this list. In the majority of points lists are identical, but some lines, individual for each series, can will cause a stir.
  • NewState — a status to which it is necessary to transfer a clocking signal. Or DISABLE — to turn off clocking, or ENABLE — to include clocking.
Let's remember that our LED is connected to PC1. It is not difficult to guess that in our case function will look so.
RST_CLK_PCLKcmd(RST_CLK_PCLK_PORTC, ENABLE);	 // Включаем тактирование порта C.
Now on an input/output port necessary to us the clock signal is given and we can begin it to configure. For a start we need to find function which configures port. It is in the MDR32F9Qx_port.h file. PORT_Init is called and has the following appearance.
void PORT_Init(MDR_PORT_TypeDef* PORTx, const PORT_InitTypeDef* PORT_InitStruct);
As we see, at this function also 2 parameters:
  1. MDR_PORT_TypeDef — a name of port which we configure. In the MDR_PORTX format, where instead of X — a letter of our port (A, B, C...). In our case there will be MDR_PORTC.
  2. The second parameter is a structure of a type of PORT_InitTypeDef. Its description is in the same file (MDR32F9Qx_port.h). Unfortunately, the description of SPL completely in English. As a result, to the person who is not knowing English and unfamiliar with the periphery device at the level of registers it will be quite heavy. And sometimes about true value of comments to functions it is only necessary to guess at first. The understanding of their assignment comes only after careful studying of the flowchart of this or that peripheral module.
    Structure declaration of PORT_InitTypeDef
    typedef struct
    {
      uint16_t PORT_Pin;                     /*!< Specifies PORT pins to be configured.
                                                  This parameter is a mask of @ref PORT_pins_define values. */
      PORT_OE_TypeDef PORT_OE;               /*!< Specifies in/out mode for the selected pins.
                                                  This parameter is one of @ref PORT_OE_TypeDef values. */
      PORT_PULL_UP_TypeDef PORT_PULL_UP;     /*!< Specifies pull up state for the selected pins.
                                                  This parameter is one of @ref PORT_PULL_UP_TypeDef values. */
      PORT_PULL_DOWN_TypeDef PORT_PULL_DOWN; /*!< Specifies pull down state for the selected pins.
                                                  This parameter is one of @ref PORT_PULL_DOWN_TypeDef values. */
      PORT_PD_SHM_TypeDef PORT_PD_SHM;       /*!< Specifies SHM state for the selected pins.
                                                  This parameter is one of @ref PORT_PD_SHM_TypeDef values. */
      PORT_PD_TypeDef PORT_PD;               /*!< Specifies PD state for the selected pins.
                                                  This parameter is one of @ref PORT_PD_TypeDef values. */
      PORT_GFEN_TypeDef PORT_GFEN;           /*!< Specifies GFEN state for the selected pins.
                                                  This parameter is one of @ref PORT_GFEN_TypeDef values. */
      PORT_FUNC_TypeDef PORT_FUNC;           /*!< Specifies operating function for the selected pins.
                                                  This parameter is one of @ref PORT_FUNC_TypeDef values. */
      PORT_SPEED_TypeDef PORT_SPEED;         /*!< Specifies the speed for the selected pins.
                                                  This parameter is one of @ref PORT_SPEED_TypeDef values. */
      PORT_MODE_TypeDef PORT_MODE;           /*!< Specifies the operating mode for the selected pins.
                                                  This parameter is one of @ref PORT_MODE_TypeDef values. */
    }PORT_InitTypeDef;
    Explanation: what is structure as to fill it from where to take values?
    The structure, in fact, is an array, everyone fixed (takes the place in an array) which cell contains some parameter. In difference from an array, each parameter of structure can have the type. As well as the array, before filling, structure needs to be created.
    PORT_InitTypeDef Led0PortC_structInit;		// На порту C.
    Here PORT_InitTypeDef is a type. In other words — just a template on the basis of which there is memory "marking". Led0PortC_structInit — the name of specific structure which is thought up by us. As creating an uint32_t variable we set her name, for example Loop, and here we create structure like PORT_InitTypeDef with the name Led0PortC_structInit. It is important to note that the declaration of structure has to be made in function to the first command. Otherwise the project will not gather. After creation of structure it is necessary to fill it. Filling goes as follows.
    имя_структуры.ее_параметр = какое-то значение;
    And so — for each parameter from the description. In the description to each cell there is an explanation what values can be written in it. As a rule, if value is not some any number from any range, then in the description there is a word ref. By means of the word standing after it it is possible to find in the file all available values for this cell. Let's take in an example the first cell.
    uint16_t PORT_Pin;                     /*!< Specifies PORT pins to be configured.
                                                  This parameter is a mask of @ref PORT_pins_define values. */
    Using search, we find PORT_pins_define.
    We see the following.
    #define PORT_Pin_0                  0x0001U  /*!< Pin 0 selected */
    #define PORT_Pin_1                  0x0002U  /*!< Pin 1 selected */
    #define PORT_Pin_2                  0x0004U  /*!< Pin 2 selected */
    #define PORT_Pin_3                  0x0008U  /*!< Pin 3 selected */
    #define PORT_Pin_4                  0x0010U  /*!< Pin 4 selected */
    #define PORT_Pin_5                  0x0020U  /*!< Pin 5 selected */
    #define PORT_Pin_6                  0x0040U  /*!< Pin 6 selected */
    #define PORT_Pin_7                  0x0080U  /*!< Pin 7 selected */
    #define PORT_Pin_8                  0x0100U  /*!< Pin 8 selected */
    #define PORT_Pin_9                  0x0200U  /*!< Pin 9 selected */
    #define PORT_Pin_10                 0x0400U  /*!< Pin 10 selected */
    #define PORT_Pin_11                 0x0800U  /*!< Pin 11 selected */
    #define PORT_Pin_12                 0x1000U  /*!< Pin 12 selected */
    #define PORT_Pin_13                 0x2000U  /*!< Pin 13 selected */
    #define PORT_Pin_14                 0x4000U  /*!< Pin 14 selected */
    #define PORT_Pin_15                 0x8000U  /*!< Pin 15 selected */
    #define PORT_Pin_All                0xFFFFU  /*!< All pins selected */
    We have PORTC an output 1. On ideas, we can write
    Led0PortC_structInit.PORT_Pin				= PORT_Pin_1;
    But we since last article had such define.
    // Подключение светодиодов.	
    #define LED0						(1<<0)			// PORTC.
    #define LED1						(1<<1)			// PORTC.
    It is the same mask of port, as in the description, only with other name. But it gives more fair idea of what we connect so I will write it.
    Led0PortC_structInit.PORT_Pin				= LED1;	// Пин нашего светодиода.
Having filled structure it will be necessary only to specify it as the function parameter SPL PORT_Init, without forgetting about "&" before a name of structure (we transfer the pointer to structure).
Let's receive ready function of a type.
void initPinPortCForLedSPL (void)
{
	PORT_InitTypeDef Led0PortC_structInit; // На порту C.
	RST_CLK_PCLKcmd(RST_CLK_PCLK_PORTC, ENABLE); // Включаем тактирование порта C.
	
	Led0PortC_structInit.PORT_Pin = LED1; // Пин нашего светодиода.
	Led0PortC_structInit.PORT_FUNC = PORT_FUNC_PORT; // Вывод работают в режиме обычного порта.
	Led0PortC_structInit.PORT_GFEN = PORT_GFEN_OFF; // Входной фильтр отключен.
	Led0PortC_structInit.PORT_MODE = PORT_MODE_DIGITAL; // Вывод цифровой.
	Led0PortC_structInit.PORT_OE = PORT_OE_OUT; // Вывод работает на выход.
	Led0PortC_structInit.PORT_PD = PORT_PD_DRIVER; // Управляемый драйвер.
	Led0PortC_structInit.PORT_PD_SHM = PORT_PD_SHM_OFF; // Триггер Шмитта выключен.
	Led0PortC_structInit.PORT_PULL_DOWN = PORT_PULL_DOWN_OFF; // Подтяжка в 0 отключена.
	Led0PortC_structInit.PORT_PULL_UP = PORT_PULL_UP_OFF; // Подтяжка в 1 отключена. 
	Led0PortC_structInit.PORT_SPEED = PORT_SPEED_MAXFAST;	 // Работа вывода с максимальной скоростью.

	PORT_Init(MDR_PORTC, &Led0PortC;_structInit); // Инициализируем порт.
}
Function of initialization of keys takes place similarly. A difference only that we specify an output mode instead of an output — an input (PORT_OE_IN), and also we turn on the inlet filter (PORT_GFEN_ON).
Function of initialization of the outputs connected to buttons.
void initPinForButtonSPL (void)
{
	// Генерируем структуры инициализации портов.
	PORT_InitTypeDef buttonPortB_structInit; // Структура для иницализации входов кнопоки на порту C.
	PORT_InitTypeDef buttonPortC_structInit; // Выходы на порту C.
	PORT_InitTypeDef buttonPortE_structInit; // Не порту E.
	
	// Включаем тактирование портов.
	RST_CLK_PCLKcmd(RST_CLK_PCLK_PORTB, ENABLE); // Включаем тактирование порта B.
	RST_CLK_PCLKcmd(RST_CLK_PCLK_PORTC, ENABLE); // Включаем тактирование порта C.
	RST_CLK_PCLKcmd(RST_CLK_PCLK_PORTE, ENABLE); // Включаем тактирование порта E.
	
	// Заполняем стркутуры портов.
	buttonPortB_structInit.PORT_FUNC = PORT_FUNC_PORT; // Выводы работают в режиме обычного порта.
	buttonPortB_structInit.PORT_GFEN = PORT_GFEN_ON; // Входной фильтр отключен на обоих выводах.
	buttonPortB_structInit.PORT_MODE = PORT_MODE_DIGITAL; // Оба вывода цифровые.
	buttonPortB_structInit.PORT_OE = PORT_OE_IN; // Выводы работают на вход.
	buttonPortB_structInit.PORT_PD = PORT_PD_DRIVER; // Управляемый драйвер.
	buttonPortB_structInit.PORT_PD_SHM = PORT_PD_SHM_OFF; // Триггер Шмитта выключен.
	buttonPortB_structInit.PORT_Pin = UP_MSK|RIGHT_MSK; // Все вышеупомянутые настройки только для двух выводов.
	buttonPortB_structInit.PORT_PULL_DOWN = PORT_PULL_DOWN_OFF; // Подтяжка в 0 отключена.
	buttonPortB_structInit.PORT_PULL_UP = PORT_PULL_UP_OFF; // Подтяжка в 1 отключена. 
	buttonPortB_structInit.PORT_SPEED = PORT_SPEED_MAXFAST; // Работа выводов с максимальной скоростью.
	
	buttonPortC_structInit.PORT_FUNC = PORT_FUNC_PORT; // PORTC.
	buttonPortC_structInit.PORT_GFEN = PORT_GFEN_ON;			
	buttonPortC_structInit.PORT_MODE = PORT_MODE_DIGITAL;	
	buttonPortC_structInit.PORT_OE = PORT_OE_IN;						
	buttonPortC_structInit.PORT_PD = PORT_PD_DRIVER;			
	buttonPortC_structInit.PORT_PD_SHM = PORT_PD_SHM_OFF;		
	buttonPortC_structInit.PORT_Pin = SELECT_MSK;						
	buttonPortC_structInit.PORT_PULL_DOWN = PORT_PULL_DOWN_OFF;		
	buttonPortC_structInit.PORT_PULL_UP 	= PORT_PULL_UP_OFF;			
	buttonPortC_structInit.PORT_SPEED = PORT_SPEED_MAXFAST;		

	buttonPortE_structInit.PORT_FUNC = PORT_FUNC_PORT; // PORTE.
	buttonPortE_structInit.PORT_GFEN = PORT_GFEN_ON;							
	buttonPortE_structInit.PORT_MODE = PORT_MODE_DIGITAL;		
	buttonPortE_structInit.PORT_OE = PORT_OE_IN;					
	buttonPortE_structInit.PORT_PD = PORT_PD_DRIVER;				
	buttonPortE_structInit.PORT_PD_SHM = PORT_PD_SHM_OFF;			
	buttonPortE_structInit.PORT_Pin = DOWN_MSK|LEFT_MSK;				
	buttonPortE_structInit.PORT_PULL_DOWN = PORT_PULL_DOWN_OFF;		
	buttonPortE_structInit.PORT_PULL_UP = PORT_PULL_UP_OFF;			
	buttonPortE_structInit.PORT_SPEED = PORT_SPEED_MAXFAST;		

	// Инициализируем порты.
	PORT_Init(MDR_PORTB, &buttonPortB;_structInit);
	PORT_Init(MDR_PORTC, &buttonPortC;_structInit);
	PORT_Init(MDR_PORTE, &buttonPortE;_structInit);
}

Setup of the timer for generation of PWM (PWM).

Before configuring the timer — we will configure an output of an input/output port to which we will bring PWM in the mode of alternative function. We remember that in last article we used PORTA port and an output 1.
There will be a following.
RST_CLK_PCLKcmd(RST_CLK_PCLK_PORTA, ENABLE); // Включаем тактирование порта A.
	
	PWMPortA_structInit.PORT_FUNC = PORT_FUNC_ALTER; // Вывод работают в режиме альтернативной функции.
	PWMPortA_structInit.PORT_GFEN = PORT_GFEN_OFF; // Входной фильтр отключен.
	PWMPortA_structInit.PORT_MODE = PORT_MODE_DIGITAL; // Вывод цифровой.
	PWMPortA_structInit.PORT_OE = PORT_OE_OUT; // Вывод работает на выход.
	PWMPortA_structInit.PORT_PD = PORT_PD_DRIVER; // Управляемый драйвер.
	PWMPortA_structInit.PORT_PD_SHM = PORT_PD_SHM_OFF; // Триггер Шмитта выключен.
	PWMPortA_structInit.PORT_Pin = PORT_Pin_1; // Пин нашего светодиода.
	PWMPortA_structInit.PORT_PULL_DOWN = PORT_PULL_DOWN_OFF; // Подтяжка в 0 отключена.
	PWMPortA_structInit.PORT_PULL_UP = PORT_PULL_UP_OFF; // Подтяжка в 1 отключена. 
	PWMPortA_structInit.PORT_SPEED = PORT_SPEED_MAXFAST;	 // Работа вывода с максимальной скоростью.

	PORT_Init(MDR_PORTA, &PWMPortA;_structInit); // Инициализируем порт.
Now we can start setup of the timer. First of all, we need to give clocking on TIMER1. It is possible to make it also by means of the RST_CLK_PCLKcmd function considered earlier.
RST_CLK_PCLKcmd(RST_CLK_PCLK_TIMER1, ENABLE); // Включаем тактирование таймера 1.
Further it is worth designating a task clearly. It is necessary for us:
  1. To configure the main timer.
  2. To configure the channel of the timer.
  3. To configure a timer output.
  4. To configure clock frequency for operation of all timer.
  5. To turn on the timer.
For each point in SPL there is the function, and for the first three points there are also structures. All functions and their parameters are in the MDR32F9Qx_timer.h file. Let's begin with the first point. For initialization of the main timer there is a TIMER_CntInit function.
void TIMER_CntInit(MDR_TIMER_TypeDef* TIMERx, const TIMER_CntInitTypeDef* TIMER_CntInitStruct);
There are two parameters.
  • TIMERx — the timer selected for initialization. It is specified in the MDR_TIMERX format where X — number of the necessary timer. In our case — MDR_TIMER1.
  • The structure like TIMER_CntInitTypeDef — in this structure is available transfer of all possible parameters of the main timer.
    There is its description.
    typedef struct {

    #if defined(USE_MDR1986VE9x)/*For Cortex M3 */
    uint16_t TIMER_IniCounter; / *! <Specifies the initial counter value.
    This parameter can be a number between 0x0000 and 0xFFFF. */
    #elif ((defined (USE_MDR1986VE3)) || (defined (USE_MDR1986VE1T)))
    uint32_t TIMER_IniCounter; / *! <Specifies the initial counter value.
    This parameter can be a number between 0x0000 and 0xFFFFFFFF. */
    #endif//#elif ((defined (USE_MDR1986VE3)) || (defined (USE_MDR1986VE1T)))

    uint16_t TIMER_Prescaler; / *! <Specifies the prescaler value used to divide the TIMER clock.
    This parameter can be a number between 0x0000 and 0xFFFF.
    CLK = TIMER_CLK / (TIMER_Prescaler + 1) */

    #if defined(USE_MDR1986VE9x)/*For Cortex M3 */
    uint16_t TIMER_Period; / *! <Specifies the period value to be loaded into the
    Auto-Reload Register (ARR) at the next update event.
    This parameter must be a number between 0x0000 and 0xFFFF. */
    #elif ((defined (USE_MDR1986VE3)) || (defined (USE_MDR1986VE1T)))/* For Cortex M1 */
    uint32_t TIMER_Period; / *! <Specifies the period value to be loaded into the
    Auto-Reload Register (ARR) at the next update event.
    This parameter must be a number between 0x0000 and 0xFFFFFFFF. */
    #endif//#elif ((defined (USE_MDR1986VE3)) || (defined (USE_MDR1986VE1T)))/* For Cortex M1 */

    uint16_t TIMER_CounterMode; / *! <Specifies the counter mode.
    This parameter can be a value of ref TIMER_Counter_Mode */

    uint16_t TIMER_CounterDirection; / *! <Specifies the counter direction.
    This parameter can be a value of ref TIMER_Counter_Direction */

    uint16_t TIMER_EventSource; / *! <Specifies the Counter Event source.
    This parameter can be a value of ref TIMER_Event_Source */

    uint16_t TIMER_FilterSampling; / *! <Specifies the filter sampling clock (FDTS).
    This parameter can be a value of ref TIMER_Filter_Sampling */

    uint16_t TIMER_ARR_UpdateMode; / *! <Specifies the Auto-Reload Register (ARR) updating mode.
    This parameter can be a value of ref TIMER_ARR_Update_Mode */

    uint16_t TIMER_ETR_FilterConf; / *! <Specifies the ETR Filter configuration.
    This parameter can be a value of ref TIMER_FilterConfiguration */

    uint16_t TIMER_ETR_Prescaler; / *! <Specifies the ETR Prescaler configuration.
    This parameter can be a value of ref TIMER_ETR_Prescaler */

    uint16_t TIMER_ETR_Polarity; / *! <Specifies the ETR Polarity configuration.
    This parameter can be a value of ref TIMER_ETR_Polarity */

    uint16_t TIMER_BRK_Polarity; / *! <Specifies the BRK Polarity configuration.
    This parameter can be a value of ref TIMER_BRK_Polarity */
    } TIMER_CntInitTypeDef;
Having filled all fields and having initialized the timer we will receive the following.

TIMER_CntInitTypeDef timerPWM_structInit; // Структура для настройки основного таймера (без каналов).
// Заполняем структуру таймера.
timerPWM_structInit.TIMER_ARR_UpdateMode = TIMER_ARR_Update_Immediately; // Регистр ARR можно обновлять в любое время.
timerPWM_structInit.TIMER_BRK_Polarity = TIMER_BRKPolarity_NonInverted; // BRK сигнал не инвертируется (нас этот параметр не касается).
timerPWM_structInit.TIMER_CounterDirection = TIMER_CntDir_Up; // Считаем "вверх". CNT инкрементируется (CNT++). 
timerPWM_structInit.TIMER_CounterMode = TIMER_CntMode_ClkFixedDir; // Считаем в одну сторону, вверх.
timerPWM_structInit.TIMER_ETR_FilterConf = TIMER_Filter_1FF_at_TIMER_CLK; // Сигнал зафиксирован в 1-м триггере на частоте TIM_CLK (В нашем случае оставляем по-умолчанию).
timerPWM_structInit.TIMER_ETR_Polarity = TIMER_ETRPolarity_NonInverted; // ETR на входе не инвертируется (мы его и не используем).
timerPWM_structInit.TIMER_ETR_Prescaler = TIMER_ETR_Prescaler_None; // Частота ETR на входе не делится (ETR не используем.).
timerPWM_structInit.TIMER_EventSource = TIMER_EvSrc_None; // Таймер не вызывает прерываний.
timerPWM_structInit.TIMER_FilterSampling = TIMER_FDTS_TIMER_CLK_div_1; // FDTS = TIMER_CLK. (Так не используем.).
timerPWM_structInit.TIMER_IniCounter = 0; // Считаем с 0. Начальное значение счетчика. (CNT = 0.).
timerPWM_structInit.TIMER_Period = PWM_speed; // Считаем до указанного в параметрах функции значения (ARR = PWM_speed).
timerPWM_structInit.TIMER_Prescaler = 32000 - 1;// Делитель входного сигнала. PSG регистр. 
	
TIMER_CntInit(MDR_TIMER1, &timerPWM;_structInit); // Инициализируем основной таймер.
As it is possible to see from code comments of filling of structure — the majority of points remain by default (disconnected). But despite it, from it is all the same necessary to specify.
Further it is necessary to initialize the channel of the timer. In our case — the first. The TIMER_ChnInit function is responsible for an initsializition of channels.
void TIMER_ChnInit(MDR_TIMER_TypeDef* TIMERx, const TIMER_ChnInitTypeDef* TIMER_ChnInitStruct)
The name of the initialized timer goes the first parameter. It remains to the same, as at function of initialization of the main timer. And here structure already TIMER_ChnInitTypeDef type.
There is its description.
typedef struct
{
uint16_t TIMER_CH_Number; / *! <Specifies the TIMER Channel number to be configured.
This parameter can be a value of ref TIMER_CH_Number */

uint16_t TIMER_CH_Mode; / *! <Specifies the TIMER Channel mode.
This parameter can be a value of ref TIMER_CH_Mode */

uint16_t TIMER_CH_ETR_Ena; / *! <Enables or disables ETR.
This parameter can be a value of FunctionalState */

uint16_t TIMER_CH_ETR_Reset; / *! <Enables or disables ETR Reset.
This parameter can be a value of ref TIMER_CH_ETR_Reset */

uint16_t TIMER_CH_BRK_Reset; / *! <Enables or disables BRK Reset.
This parameter can be a value of ref TIMER_CH_BRK_Reset */

uint16_t TIMER_CH_REF_Format; / *! <Specifies the REF signal format.
This parameter can be a value of ref TIMER_CH_REF_Format */

uint16_t TIMER_CH_Prescaler; / *! <Specifies the TIMER Channel Prescaler configuration.
This parameter can be a value of ref TIMER_CH_Prescaler */

uint16_t TIMER_CH_EventSource; / *! <Specifies the Channel Event source.
This parameter can be a value of ref TIMER_CH_EventSource */

uint16_t TIMER_CH_FilterConf; / *! <Specifies the TIMER Channel Filter configuration.
This parameter can be a value of ref TIMER_FilterConfiguration */

uint16_t TIMER_CH_CCR_UpdateMode; / *! <Specifies the TIMER CCR, CCR1 update mode.
This parameter can be a value of ref TIMER_CH_CCR_Update_Mode */

uint16_t TIMER_CH_CCR1_Ena; / *! <Enables or disables the CCR1 register.
This parameter can be a value of FunctionalState */

uint16_t TIMER_CH_CCR1_EventSource; / *! <Specifies the Channel CCR1 Event source.
This parameter can be a value of ref TIMER_CH_CCR1_EventSource */
} TIMER_ChnInitTypeDef;
Also we fill and we initialize.
TIMER_ChnInitTypeDef timerPWM_channelStructInit; // Структура канала ШИМ.
// Заполняем структуру PWM канала.
timerPWM_channelStructInit.TIMER_CH_BRK_Reset = TIMER_CH_BRK_RESET_Disable; // Сброс канала BRK не производится (BRK не используем).
timerPWM_channelStructInit.TIMER_CH_CCR1_Ena =	DISABLE; // CCR1 не используем.
timerPWM_channelStructInit.TIMER_CH_CCR1_EventSource =	TIMER_CH_CCR1EvSrc_PE; // Выбор события по входному каналу для CAP1: положительный фронт по Chi. (По умолчанию, мы не используем).
timerPWM_channelStructInit.TIMER_CH_CCR_UpdateMode = TIMER_CH_CCR_Update_Immediately; // Регистр CCR можно обновлять в любое время (CCR не используем).
timerPWM_channelStructInit.TIMER_CH_ETR_Ena = DISABLE; // ETR не используется.
timerPWM_channelStructInit.TIMER_CH_ETR_Reset = TIMER_CH_ETR_RESET_Disable; // Сброс ETR не производится.
timerPWM_channelStructInit.TIMER_CH_EventSource = TIMER_CH_EvSrc_PE;								// Выбор события по входному каналу: положительный фронт. (Так же не используется).
timerPWM_channelStructInit.TIMER_CH_FilterConf = TIMER_Filter_1FF_at_TIMER_CLK; // Входной сигнал от TIMER_CLK фиксируется одним триггером.
timerPWM_channelStructInit.TIMER_CH_Mode = TIMER_CH_MODE_PWM; // Канал в ШИМ режиме.
timerPWM_channelStructInit.TIMER_CH_Number = TIMER_CHANNEL1; // Первый канал. 
timerPWM_channelStructInit.TIMER_CH_Prescaler = TIMER_CH_Prescaler_None; // В канале частота не делится.
timerPWM_channelStructInit.TIMER_CH_REF_Format = TIMER_CH_REF_Format3; // Сигнал REF меняется при CNT == ARR.
	
TIMER_ChnInit(MDR_TIMER1, &timerPWM;_channelStructInit); // Инициализируем канал.
I will notice that in this function we originate a record on REF for PWM.
Further it is necessary to configure the channel of the timer on an output. For this purpose there is a TIMER_ChnOutInit function.
void TIMER_ChnOutInit(MDR_TIMER_TypeDef* TIMERx, const TIMER_ChnOutInitTypeDef* TIMER_ChnOutInitStruct);
The name of our timer goes the first parameter also. The second — structure of TIMER_ChnOutInitStruct.
Its description.
typedef struct
{
uint16_t TIMER_CH_Number; / *! <Specifies the TIMER Channel number to be configured.
This parameter can be a value of ref TIMER_CH_Number */

uint16_t TIMER_CH_DirOut_Polarity; / *! <Specifies the TIMER CHx output polarity.
This parameter can be a value of ref TIMER_CH_OUT_Polarity */

uint16_t TIMER_CH_DirOut_Source; / *! <Specifies the TIMER CHx output source.
This parameter can be a value of ref TIMER_CH_OUT_Source */

uint16_t TIMER_CH_DirOut_Mode; / *! <Specifies the TIMER CHx output enable source.
This parameter can be a value of ref TIMER_CH_OUT_Mode */

uint16_t TIMER_CH_NegOut_Polarity; / *! <Enables or disables the TIMER CHxN output inversion.
This parameter can be a value of ref TIMER_CH_OUT_Polarity */

uint16_t TIMER_CH_NegOut_Source; / *! <Specifies the TIMER CHxN output source.
This parameter can be a value of ref TIMER_CH_OUT_Source */

uint16_t TIMER_CH_NegOut_Mode; / *! <Specifies the TIMER CHxN output enable source.
This parameter can be a value of ref TIMER_CH_OUT_Mode */

uint16_t TIMER_CH_DTG_MainPrescaler; / *! <Specifies the main prescaler of TIMER DTG.
This parameter can be a number between 0x0000 and 0x00FF.
Delay DTGdel = TIMER_CH_DTG_MainPrescaler * (TIMER_CH_DTG_AuxPrescaler + 1) clocks. */

uint16_t TIMER_CH_DTG_AuxPrescaler; / *! <Specifies the auxiliary prescaler of TIMER DTG.
This parameter can be a number between 0x0000 and 0x000F.
Delay DTGdel = TIMER_CH_DTG_MainPrescaler * (TIMER_CH_DTG_AuxPrescaler + 1) clocks. */

uint16_t TIMER_CH_DTG_ClockSource; / *! <Specifies the TIMER DTG clock source.
This parameter can be a value of ref TIMER_CH_DTG_Clock_Source */
} TIMER_ChnOutInitTypeDef;
After filling of structure and initialization we will observe the following code.
TIMER_ChnOutInitTypeDef timerPWM_channelOUTPWMStructInit; // Структура настройки выхода канала ШИМ.
// Параметры выхода.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DirOut_Mode = TIMER_CH_OutMode_Output; // Всегда выход.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DirOut_Polarity = TIMER_CHOPolarity_NonInverted; // Неинвертированный.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DirOut_Source = TIMER_CH_OutSrc_REF; // На выход REF сигнал.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DTG_AuxPrescaler = 0; // Делителя не стоит.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DTG_ClockSource = TIMER_CH_DTG_ClkSrc_TIMER_CLK; // Источник тактового сигнала для DTG - TIMER_CLK. Но DTG мы все равно не используем.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DTG_MainPrescaler = 0; // Делитель сигнала на DTG.
timerPWM_channelOUTPWMStructInit.TIMER_CH_NegOut_Mode = TIMER_CH_OutMode_Input; // Инвертный канал на вход. Все остальные его параметр берем по умолчанию, т.к. они не важны.
timerPWM_channelOUTPWMStructInit.TIMER_CH_NegOut_Polarity = TIMER_CHOPolarity_NonInverted; // Без инвертирования инвертированного канала.
timerPWM_channelOUTPWMStructInit.TIMER_CH_NegOut_Source = TIMER_CH_DTG_ClkSrc_TIMER_CLK; // Источник тактового сигнала для DTG - TIMER_CLK.
timerPWM_channelOUTPWMStructInit.TIMER_CH_Number = TIMER_CHANNEL1; // Первый канал.
	
TIMER_ChnOutInit(MDR_TIMER1, &timerPWM;_channelOUTPWMStructInit); // Настраиваем канал на выход.
Now it was necessary only to give clocking on the timer and to start it. For giving of a clock signal (on the basis of what the timer keeps count) there is a TIMER_BRGInit function.
void TIMER_BRGInit(MDR_TIMER_TypeDef* TIMERx, uint32_t TIMER_BRG);
The first parameter, as usual, a timer name, the second — a divider. The divider is calculated as well as for the register PSG in the previous article (in fact this function only writes our divider to PSG...). Also I will note that the same function and permits giving of a signal of clocking on the timer by default. And the TIMER_Cmd function is responsible for inclusion.
void TIMER_Cmd(MDR_TIMER_TypeDef* TIMERx, FunctionalState NewState)
Parameters — a name of the timer and its status ENABLE/DISABLE.
On a result of all setup we receive the following.
//Initialization of the timer in the PWM mode for work with a LED in the SPL mode.
void initTimerPWMledSPL (uint32_t PWM_speed)
{
PORT_InitTypeDef PWMPortA_structInit;//Structure for initialization of an output of the timer in the PWM mode.
TIMER_CntInitTypeDef timerPWM_structInit;//Structure for setup of the main timer (without channels).
TIMER_ChnInitTypeDef timerPWM_channelStructInit;//Structure of PWM channel.
TIMER_ChnOutInitTypeDef timerPWM_channelOUTPWMStructInit;//Structure of setup of a channel output of PWM.

RST_CLK_PCLKcmd (RST_CLK_PCLK_PORTA, ENABLE);//We include clocking of port A.
RST_CLK_PCLKcmd (RST_CLK_PCLK_TIMER1, ENABLE);//We include clocking of the timer 1.

PWMPortA_structInit.PORT_FUNC = PORT_FUNC_ALTER;//An output work in the mode of alternative function.
PWMPortA_structInit.PORT_GFEN = PORT_GFEN_OFF;//The inlet filter is switched-off.
PWMPortA_structInit.PORT_MODE = PORT_MODE_DIGITAL;//Output digital.
PWMPortA_structInit.PORT_OE = PORT_OE_OUT;//The output works for an output.
PWMPortA_structInit.PORT_PD = PORT_PD_DRIVER;//The managed driver.
PWMPortA_structInit.PORT_PD_SHM = PORT_PD_SHM_OFF;//The Schmitt trigger is switched off.
PWMPortA_structInit.PORT_Pin = PORT_Pin_1;//Pin of our LED.
PWMPortA_structInit.PORT_PULL_DOWN = PORT_PULL_DOWN_OFF;//Tightening in 0 is turned off.
PWMPortA_structInit.PORT_PULL_UP = PORT_PULL_UP_OFF;//Tightening in 1 is turned off.
PWMPortA_structInit.PORT_SPEED = PORT_SPEED_MAXFAST;//Work of an output with maximum speed.

PORT_Init (MDR_PORTA, &PWMPortA;_structInit);//We initialize port.

//We fill structure of the timer.
timerPWM_structInit.TIMER_ARR_UpdateMode = TIMER_ARR_Update_Immediately;//The register ARR can be updated at any time.
timerPWM_structInit.TIMER_BRK_Polarity = TIMER_BRKPolarity_NonInverted;//BRK a signal is not inverted (this parameter does not concern us).
timerPWM_structInit.TIMER_CounterDirection = TIMER_CntDir_Up;//We consider "up". CNT is incremented (CNT ++).
timerPWM_structInit.TIMER_CounterMode = TIMER_CntMode_ClkFixedDir;//We consider in one party, up.
timerPWM_structInit.TIMER_ETR_FilterConf = TIMER_Filter_1FF_at_TIMER_CLK;//The signal is recorded in the 1st trigger at TIM_CLK frequency (In our case we leave by default).
timerPWM_structInit.TIMER_ETR_Polarity = TIMER_ETRPolarity_NonInverted;//ETR on an input is not inverted (we also do not use it).
timerPWM_structInit.TIMER_ETR_Prescaler = TIMER_ETR_Prescaler_None;//ETR frequency on an input does not share (ETR we do not use.).
timerPWM_structInit.TIMER_EventSource = TIMER_EvSrc_None;//The timer does not cause interruptions.
timerPWM_structInit.TIMER_FilterSampling = TIMER_FDTS_TIMER_CLK_div_1;//FDTS = TIMER_CLK. (So we do not use.).
timerPWM_structInit.TIMER_IniCounter = 0;//We consider with 0. Starting value of the counter. (CNT = 0.).
timerPWM_structInit.TIMER_Period = PWM_speed;//We consider to the value specified in function parameters (ARR = PWM_speed).
timerPWM_structInit.TIMER_Prescaler = 32000 — 1;//Divider of an input signal. PSG register.

TIMER_CntInit (MDR_TIMER1, &timerPWM;_structInit);//We initialize the main timer.

//We fill structure of the PWM channel.
timerPWM_channelStructInit.TIMER_CH_BRK_Reset = TIMER_CH_BRK_RESET_Disable;//Reset of BRK channel is not made (BRK we do not use).
timerPWM_channelStructInit.TIMER_CH_CCR1_Ena = DISABLE;//We do not use CCR1.
timerPWM_channelStructInit.TIMER_CH_CCR1_EventSource = TIMER_CH_CCR1EvSrc_PE;//The choice of an event through the input channel for CAP1: the positive front on Chi. (By default, we do not use).
timerPWM_channelStructInit.TIMER_CH_CCR_UpdateMode = TIMER_CH_CCR_Update_Immediately;//The register CCR can be updated at any time (CCR we do not use).
timerPWM_channelStructInit.TIMER_CH_ETR_Ena = DISABLE;//ETR is not used.
timerPWM_channelStructInit.TIMER_CH_ETR_Reset = TIMER_CH_ETR_RESET_Disable;//Reset of ETR is not made.
timerPWM_channelStructInit.TIMER_CH_EventSource = TIMER_CH_EvSrc_PE;//The choice of an event through the input channel: positive front. (Also it is not used).
timerPWM_channelStructInit.TIMER_CH_FilterConf = TIMER_Filter_1FF_at_TIMER_CLK;//The input signal from TIMER_CLK is fixed by one trigger.
timerPWM_channelStructInit.TIMER_CH_Mode = TIMER_CH_MODE_PWM;//The channel in PWM the mode.
timerPWM_channelStructInit.TIMER_CH_Number = TIMER_CHANNEL1;//Channel One.
timerPWM_channelStructInit.TIMER_CH_Prescaler = TIMER_CH_Prescaler_None;//In the channel frequency does not share.
timerPWM_channelStructInit.TIMER_CH_REF_Format = TIMER_CH_REF_Format3;//The signal of REF changes at CNT == ARR.

TIMER_ChnInit (MDR_TIMER1, &timerPWM;_channelStructInit);//We initialize the channel.

//Output parameters.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DirOut_Mode = TIMER_CH_OutMode_Output;//Always output.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DirOut_Polarity = TIMER_CHOPolarity_NonInverted;//Not inverted.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DirOut_Source = TIMER_CH_OutSrc_REF;//On REF output a signal.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DTG_AuxPrescaler = 0;//Do not cost a divider.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DTG_ClockSource = TIMER_CH_DTG_ClkSrc_TIMER_CLK;//A source of a clock signal for DTG — TIMER_CLK. But all of us equally do not use DTG.
timerPWM_channelOUTPWMStructInit.TIMER_CH_DTG_MainPrescaler = 0;//A signal divider on DTG.
timerPWM_channelOUTPWMStructInit.TIMER_CH_NegOut_Mode = TIMER_CH_OutMode_Input;//The invert channel on an input. Its parameter we take all others by default since they are not important.
timerPWM_channelOUTPWMStructInit.TIMER_CH_NegOut_Polarity = TIMER_CHOPolarity_NonInverted;//Without inverting of the inverted channel.
timerPWM_channelOUTPWMStructInit.TIMER_CH_NegOut_Source = TIMER_CH_DTG_ClkSrc_TIMER_CLK;//A source of a clock signal for DTG — TIMER_CLK.
timerPWM_channelOUTPWMStructInit.TIMER_CH_Number = TIMER_CHANNEL1;//Channel One.

TIMER_ChnOutInit (MDR_TIMER1, &timerPWM;_channelOUTPWMStructInit);//We configure the channel on an output.

TIMER_BRGInit (MDR_TIMER1, TIMER_HCLKdiv1);//We give a signal source for the account (processor frequency without predivider).//In this function the choice of a divider (at us "1") and inclusion of giving of clock period.
TIMER_Cmd (MDR_TIMER1, ENABLE);//We turn on the timer.
}

Setup of the timer for a challenge of interruptions (IRQ)

Further we need to configure the timer generating interruptions for poll of keys. Here we will need to configure the timer only on the first structure. As we do not use channels and outputs. Initialization of the timer will look the same as at previous, except for TIMER_EventSource cell. We have to specify in it on what event we have an interruption. In last article we used CNT == ARR. Also we use it.
And in general, the following options are possible.
#define TIMER_EvSrc_None                      (((uint32_t)0x0) << TIMER_CNTRL_EVENT_SEL_Pos)  /*!< No events. */
#define TIMER_EvSrc_TM1                       (((uint32_t)0x1) << TIMER_CNTRL_EVENT_SEL_Pos)  /*!< Selects TIMER1 (CNT == ARR) event. */
#define TIMER_EvSrc_TM2                       (((uint32_t)0x2) << TIMER_CNTRL_EVENT_SEL_Pos)  /*!< Selects TIMER2 (CNT == ARR) event. */
#define TIMER_EvSrc_TM3                       (((uint32_t)0x3) << TIMER_CNTRL_EVENT_SEL_Pos)  /*!< Selects TIMER3 (CNT == ARR) event. */
#define TIMER_EvSrc_CH1                       (((uint32_t)0x4) << TIMER_CNTRL_EVENT_SEL_Pos)  /*!< Selects Channel 1 event. */
#define TIMER_EvSrc_CH2                       (((uint32_t)0x5) << TIMER_CNTRL_EVENT_SEL_Pos)  /*!< Selects Channel 2 event. */
#define TIMER_EvSrc_CH3                       (((uint32_t)0x6) << TIMER_CNTRL_EVENT_SEL_Pos)  /*!< Selects Channel 3 event. */
#define TIMER_EvSrc_CH4                       (((uint32_t)0x7) << TIMER_CNTRL_EVENT_SEL_Pos)  /*!< Selects Channel 4 event. */
#define TIMER_EvSrc_ETR                       (((uint32_t)0x8) << TIMER_CNTRL_EVENT_SEL_Pos)  /*!< Selects ETR event. */
Also we will not forget about inclusion of clocking of the timer and giving on it a clock signal for the account.
Here so we initialized the timer.
TIMER_CntInitTypeDef timerButtonCheck_structInit;//Structure for setup of the main timer of a challenge of interruption for poll of keys.
RST_CLK_PCLKcmd (RST_CLK_PCLK_TIMER2, ENABLE);//We include clocking of the timer 1.
TIMER_BRGInit (MDR_TIMER2, TIMER_HCLKdiv1);//We give a signal source for the account (processor frequency without predivider).

//We fill structure of the main timer.
timerButtonCheck_structInit.TIMER_ARR_UpdateMode = TIMER_ARR_Update_Immediately;//The register ARR can be updated at any time.
timerButtonCheck_structInit.TIMER_BRK_Polarity = TIMER_BRKPolarity_NonInverted;//BRK a signal is not inverted (this parameter does not concern us).
timerButtonCheck_structInit.TIMER_CounterDirection = TIMER_CntDir_Up;//We consider "up". CNT is incremented (CNT ++).
timerButtonCheck_structInit.TIMER_CounterMode = TIMER_CntMode_ClkFixedDir;//We consider in one party, up.
timerButtonCheck_structInit.TIMER_ETR_FilterConf = TIMER_Filter_1FF_at_TIMER_CLK;//The signal is recorded in the 1st trigger at TIM_CLK frequency (In our case we leave by default).
timerButtonCheck_structInit.TIMER_ETR_Polarity = TIMER_ETRPolarity_NonInverted;//ETR on an input is not inverted (we also do not use it).
timerButtonCheck_structInit.TIMER_ETR_Prescaler = TIMER_ETR_Prescaler_None;//ETR frequency on an input does not share (ETR we do not use.).
timerButtonCheck_structInit.TIMER_EventSource = TIMER_EvSrc_TM2;//The timer causes interruption at CNT = ARR.
timerButtonCheck_structInit.TIMER_FilterSampling = TIMER_FDTS_TIMER_CLK_div_1;//FDTS = TIMER_CLK. (So we do not use.).
timerButtonCheck_structInit.TIMER_IniCounter = 0;//We consider with 0. Starting value of the counter. (CNT = 0.).
timerButtonCheck_structInit.TIMER_Period = 250/25;//We consider to the value specified in function parameters (ARR = PWM_speed).
timerButtonCheck_structInit.TIMER_Prescaler = 32000 — 1;//Divider of an input signal. PSG register.

TIMER_CntInit (MDR_TIMER2, &timerButtonCheck;_structInit);//We initialize the main timer.
Further we will use the function which is built in CMSIS for interrupt enable from all timer (we sorted it in the previous article) and we will turn on the timer.
Full function of initialization.
// Настройка таймера для генерации прерываний 25 раз в секунду при помощи SPL.
void initTimerButtonCheckSPL (void) 
{ 
 TIMER_CntInitTypeDef timerButtonCheck_structInit; // Структура для настройки основного таймера вызова прерывания для опроса клавиш.
 RST_CLK_PCLKcmd(RST_CLK_PCLK_TIMER2, ENABLE); // Включаем тактирование таймера 1.
 TIMER_BRGInit(MDR_TIMER2, TIMER_HCLKdiv1); // Подаем источник сигнала для счета (частота процессора без предделителя).
	
 // Заполняем структуру основного таймера.
 timerButtonCheck_structInit.TIMER_ARR_UpdateMode = TIMER_ARR_Update_Immediately; // Регистр ARR можно обновлять в любое время.
 timerButtonCheck_structInit.TIMER_BRK_Polarity = TIMER_BRKPolarity_NonInverted; // BRK сигнал не инвертируется (нас этот параметр не касается).
 timerButtonCheck_structInit.TIMER_CounterDirection = TIMER_CntDir_Up; // Считаем "вверх". CNT инкрементируется (CNT++). 
 timerButtonCheck_structInit.TIMER_CounterMode = TIMER_CntMode_ClkFixedDir; // Считаем в одну сторону, вверх.
 timerButtonCheck_structInit.TIMER_ETR_FilterConf = TIMER_Filter_1FF_at_TIMER_CLK; // Сигнал зафиксирован в 1-м триггере на частоте TIM_CLK (В нашем случае оставляем по-умолчанию).
 timerButtonCheck_structInit.TIMER_ETR_Polarity = TIMER_ETRPolarity_NonInverted; // ETR на входе не инвертируется (мы его и не используем).
 timerButtonCheck_structInit.TIMER_ETR_Prescaler = TIMER_ETR_Prescaler_None; // Частота ETR на входе не делится (ETR не используем.).
 timerButtonCheck_structInit.TIMER_EventSource = TIMER_EvSrc_TM2; // Таймер вызывает прерывание при CNT = ARR.
 timerButtonCheck_structInit.TIMER_FilterSampling = TIMER_FDTS_TIMER_CLK_div_1; // FDTS = TIMER_CLK. (Так не используем.).
 timerButtonCheck_structInit.TIMER_IniCounter = 0; // Считаем с 0. Начальное значение счетчика. (CNT = 0.).
 timerButtonCheck_structInit.TIMER_Period = 250/25;																// Считаем до указанного в параметрах функции значения (ARR = PWM_speed).
 timerButtonCheck_structInit.TIMER_Prescaler = 32000 - 1; // Делитель входного сигнала. PSG регистр. 

 TIMER_CntInit(MDR_TIMER2, &timerButtonCheck;_structInit); // Инициализируем основной таймер.
	
 TIMER_ITConfig(MDR_TIMER2, TIMER_STATUS_CNT_ARR, ENABLE); // Разрешаем прерывание по CNT = ARR.	
 NVIC_EnableIRQ(Timer2_IRQn); // Разрешаем прерывание от таймера в целом.
	
 TIMER_Cmd(MDR_TIMER2, ENABLE); // Включаем таймер.
}

We transfer interruption to SPL.

Transfer on SPL functions in interruption will be the last step. Interruption has the same standard name entered in a startup the file. We remember that at an input in interruption we need to reset a flag of the status of the timer. For this purpose the TIMER_ClearFlag function serves.
void TIMER_ClearFlag(MDR_TIMER_TypeDef* TIMERx, uint32_t Flags)
As parameters it is required to enter a name of port and an interrupt flag. In our case will be:
TIMER_ClearFlag(MDR_TIMER2, TIMER_STATUS_CNT_ARR); // Сбрасываем флаг. Обязательно первой коммандой.
After that we inverted a LED status, showing that interruption worked. In SPL there is no function of inverting of bit, but there is a function of a read and write of single bits. We will also use them.
uint8_t PORT_ReadInputDataBit(MDR_PORT_TypeDef* PORTx, uint32_t PORT_Pin);
void PORT_WriteBit(MDR_PORT_TypeDef* PORTx, uint32_t PORT_Pin, BitAction BitVal);
At the both first parameter there is a name of port, further at function of reading it is necessary to enter a pin name. The mask is specified. At record function, after a name of port it is necessary to specify bit which registers (also a mask) and value of bit (0 or 1). Function of reading can be used as record function parameter. Then we will receive:
PORT_WriteBit(MDR_PORTC, LED1, !PORT_ReadInputDataBit(MDR_PORTC, LED1)); // Записываем инвертированное значение бита.
After that we need to read out data from buttons. And by one button. For this purpose we will use the PORT_ReadInputDataBit function investigated above.
Poll of buttons will look so.
if (PORT_ReadInputDataBit(MDR_PORTB, UP_MSK) == 0) PWM_speed--; // Проверяем, нажата ли какая-нибудь клавиша. Если нажата - что-то делаем с частотой.
		else if (PORT_ReadInputDataBit(MDR_PORTE, DOWN_MSK) == 0) PWM_speed++;			
		else if (PORT_ReadInputDataBit(MDR_PORTE, LEFT_MSK) == 0) PWM_speed--;
		else if (PORT_ReadInputDataBit(MDR_PORTB, RIGHT_MSK)== 0) PWM_speed++;
It was necessary only to replace frequency upon termination of poll of buttons. For this purpose there is a TIMER_SetCntAutoreload function.
void TIMER_SetCntAutoreload(MDR_TIMER_TypeDef* TIMERx, uint16_t Autoreload)
We need to specify only the timer about PWM and new frequency.
On a result we have interruption of a type.
void Timer2_IRQHandler (void)
{
	TIMER_ClearFlag(MDR_TIMER2, TIMER_STATUS_CNT_ARR); // Сбрасываем флаг. Обязательно первой коммандой.																										
	PORT_WriteBit(MDR_PORTC, LED1, !PORT_ReadInputDataBit(MDR_PORTC, LED1)); // Записываем инвертированное значение бита.		
	if (PORT_ReadInputDataBit(MDR_PORTB, UP_MSK) == 0) PWM_speed--; // Проверяем, нажата ли какая-нибудь клавиша. Если нажата - что-то делаем с частотой.
		else if (PORT_ReadInputDataBit(MDR_PORTE, DOWN_MSK) == 0) PWM_speed++;			
		else if (PORT_ReadInputDataBit(MDR_PORTE, LEFT_MSK) == 0) PWM_speed--;
		else if (PORT_ReadInputDataBit(MDR_PORTB, RIGHT_MSK)== 0) PWM_speed++;
	
	// Проверяем, чтобы частота не вышла за пределы диапазона от 250 Гц до 0.5 Гц.
	if (PWM_speed < 1) PWM_speed = 1;									
		else if (PWM_speed > 500) PWM_speed = 500;
	
	TIMER_SetCntAutoreload(MDR_TIMER1, PWM_speed); // Меняем частоту. 					
}

Summing up.


As we could be convinced, with use a SPL code to look became much more volume. But let's compare the weight of the received code. In the code written only with use of CMSIS with all types of optimization occupies so much.
We pass from STM32 to the Russian K1986BE92QI microcontroller. Practical application: We poll keys, we generate PWM. Comparison of a code on CMSIS and SPL (PWM+TIM+PORT). Part second
Our code with optimization - O0 weighs so much.
We pass from STM32 to the Russian K1986BE92QI microcontroller. Practical application: We poll keys, we generate PWM. Comparison of a code on CMSIS and SPL (PWM+TIM+PORT). Part second
With optimization - O3.
We pass from STM32 to the Russian K1986BE92QI microcontroller. Practical application: We poll keys, we generate PWM. Comparison of a code on CMSIS and SPL (PWM+TIM+PORT). Part second
The code with use of SPL weighs in 4-5 times more, than written manually. About execution speed I in general am silent. It is a subject of separate article which there is a lot of. Now we will sum up the results.
Pluses of use of SPL
  1. Written by means of a SPL code it is perceived unambiguously, in the presence of the corresponding description to SPL (what yet, unfortunately, is not present).
  2. At the correct filling of structures — everything will be configured truly. Lack of errors. I did not manage to find a uniform error in SPL yet. Even in spite of the fact that it is the B-version.

Minuses of use of SPL
  1. Code size blows the mind.
  2. Speed of execution is also lower.
  3. At the moment it is more difficult to deal with SPL, than with CMSIS.
  4. Not always it is possible to guess logic of the person who wrote library. Occasionally nonserviceability is explained by the fact that what had to be included in function — is taken out from it in the friend.
  5. Huge number of the place of code lines for initialization of structures.

Output

SPL has the right to life, but only after all its opportunities are clearly described. And it is possible to use it only in tasks where a lot of computational capability and memory size is not required. Personally it was much simpler to me to configure all described periphery, reading documentation and passing not the necessary registers.

File of the project.


This article is a translation of the original post at habrahabr.ru/post/270863/
If you have any questions regarding the material covered in the article above, please, contact the original author of the post.
If you have any complaints about this article or you want this article to be deleted, please, drop an email here: sysmagazine.com@gmail.com.

We believe that the knowledge, which is available at the most popular Russian IT blog habrahabr.ru, should be accessed by everyone, even though it is poorly translated.
Shared knowledge makes the world better.
Best wishes.

comments powered by Disqus