Пример модуля для аппаратного ШИМ для процессоров STM32F4

Программа формирует на выводах A0, A1, A2, A3 широтно-импульную модуляцию различной скважности.

MODULE Main;

	IMPORT SYSTEM,
		ARMv7M := MicroARMv7M,
		MCU := MicroSTM32F4,
		Pins := MicroSTM32F4Pins,
		Sys := MicroSTM32F4System,
		SysTick0 := MobxARMv7MSTM32SysTick0,
		PWM;

	PROCEDURE Init;
	BEGIN
		Pins.Configure(Pins.C, 13,
			Pins.output, Pins.pushPull, Pins.medium, Pins.noPull, Pins.AF0);
		SysTick0.Init(Sys.HCLK, 1000);
		
		PWM.InitTIM5(
			5000 (* freq *),
			1 (* prescaler *)
		);
		
		(* positions = 80000000 / 5000 / 1  = 16000 *)
		
		PWM.SetTIM5(0, 12000); (* 75 % *)
		PWM.SetTIM5(1, 8000); (* 50 % *)
		PWM.SetTIM5(2, 4000); (* 25 % *)
		PWM.SetTIM5(3, 2000); (* 12.5 % *)
		
	END Init;
	
	PROCEDURE MainLoop;
	VAR ms: INTEGER; x: SET;
	BEGIN
		ms := 0;
		REPEAT
			IF SysTick0.OnTimer() THEN
				IF ms = 999 THEN ms := 0 ELSE INC(ms) END;
				IF ms = 0 THEN
					SYSTEM.GET(MCU.GPIOCBSRR, x);
					SYSTEM.PUT(MCU.GPIOCBSRR, x + {13+16})
				ELSIF ms = 500 THEN
					SYSTEM.GET(MCU.GPIOCBSRR, x);
					SYSTEM.PUT(MCU.GPIOCBSRR, x + {13})
				END
			END;
			ARMv7M.WFI
		UNTIL FALSE
	END MainLoop;

BEGIN
	Init;
	MainLoop

END Main.
MODULE PWM;
	(*
		Alexander Shiryaev, 2015.04
		Ivan Denisov, 2021.12.08
		
		TIM2 / TIM5
		PA0 (CH1)
		PA1 (CH2)
		PA2 (CH3)
		PA3 (CH4)
	*)

	IMPORT
		SYSTEM,
		ARMv7M := MicroARMv7M,
		MCU := MicroSTM32F4,
		Sys := MicroSTM32F4System,
		PinCfg := MicroSTM32F4Pins;

	VAR
		positions*: INTEGER;
		tSigMin0: INTEGER;
	
	PROCEDURE SetTIM2* (chn: INTEGER; pos: INTEGER);
	BEGIN
		ASSERT(chn >= 0);
		ASSERT(chn < 4);
		ASSERT(pos >= 0);
		ASSERT(pos < positions);
		SYSTEM.PUT(MCU.TIM2CCR1 + 4 * chn, tSigMin0 + pos)
	END SetTIM2;

	PROCEDURE InitTIM2*(period, prescaler: INTEGER);
	
		CONST
				(* 80000000 / period / prescaler = 64000 < 2^32,
					2^16 = 65536
					2^32 = 4294967296
				*)

			(* NVIC *)
				ISER = ARMv7M.NVICISER0 + (MCU.TIM2Int DIV 32) * 4;
				ICER = ARMv7M.NVICICER0 + (MCU.TIM2Int DIV 32) * 4;
				int = MCU.TIM2Int MOD 32;
				
			(* RCCAPB1[LP]ENR bits: *)
				TIM2EN = 0;
			(* TIM1CR1 bits: *)
				CEN = 0;
				UDIS = 1;
				URS = 2;
				OPM = 3;
				DIR = 4;
				ARPE = 7;
			(* TIM1DIER bits: *)
				UIE = 0;
				CC1IE = 1; CC2IE = 2; CC3IE = 3; CC4IE = 4;
				COMIE = 5;
				TIE = 6;
				BIE = 7;
			(* TIM1CCER bits: *)
				CC1E = 0; CC1P = 1;
				CC2E = 4; CC2P = 5;
				CC3E = 8; CC3P = 9;
				CC4E = 12; CC4P = 13;
			direct = {}; inverse = {CC1P,CC2P,CC3P,CC4P}; polarity = direct;
			(* TIM1CCMR1 bits: *)
				OC1FE = 2; OC1PE = 3; OC1CE = 7;
				OC2FE = 8 + 2; OC2PE = 8 + 3; OC2CE = 8 + 7;
			(* TIM1CCMR2 bits: *)
				OC3FE = 2; OC3PE = 3; OC3CE = 7;
				OC4FE = 8 + 2; OC4PE = 8 + 3; OC4CE = 8 + 7;

		VAR x: SET; tFrame: INTEGER;
		
	BEGIN
		
		(* disable interrupts *)
			SYSTEM.PUT(ICER, {int}); ARMv7M.ISB;
			SYSTEM.PUT(MCU.TIM2DIER, {});
			
		(* NOTE: timer frequency = 2 * APB1 frequency *)
			tFrame := Sys.TIMCLK1 DIV ( period * prescaler);
			positions := Sys.TIMCLK1 DIV ( period * prescaler);
			tSigMin0 := 0;
			
			(*
			positions := (3 * Sys.PCLK1) DIV (divider * 1250); (* 1.2 msec *)
			tSigMin0 := (9 * Sys.PCLK1) DIV (divider * 5000); (* 900 usec *)
			*)
			
		(* disable timer counter *)
			SYSTEM.GET(MCU.TIM2CR1, x);
			SYSTEM.PUT(MCU.TIM2CR1, x - {CEN});
		
		(* disable capture/compare channels *)
			SYSTEM.PUT(MCU.TIM2CCER, polarity);

		(* reset timer counter *)
			SYSTEM.PUT(MCU.TIM2CNT, 0);

		(* enable TIM2 clock *)
			SYSTEM.GET(MCU.RCCAPB1ENR, x);
			SYSTEM.PUT(MCU.RCCAPB1ENR, x + {TIM2EN});

			SYSTEM.GET(MCU.RCCAPB1LPENR, x);
			SYSTEM.PUT(MCU.RCCAPB1LPENR, x + {TIM2EN});
		
		(* configure PWM pins *)
			PinCfg.Configure(PinCfg.A, 0,
				PinCfg.alt, PinCfg.pushPull, PinCfg.low, PinCfg.noPull, PinCfg.AF1);
			PinCfg.Configure(PinCfg.A, 1,
				PinCfg.alt, PinCfg.pushPull, PinCfg.low, PinCfg.noPull, PinCfg.AF1);
			PinCfg.Configure(PinCfg.A, 2,
				PinCfg.alt, PinCfg.pushPull, PinCfg.low, PinCfg.noPull, PinCfg.AF1);
			PinCfg.Configure(PinCfg.A, 3,
				PinCfg.alt, PinCfg.pushPull, PinCfg.low, PinCfg.noPull, PinCfg.AF1);
		
		(* timer setup *)
			(* setup prescaler *)
				SYSTEM.PUT(MCU.TIM2PSC, prescaler - 1);
			(* edge-aligned mode *)
				SYSTEM.GET(MCU.TIM2CR1, x);
				SYSTEM.PUT(MCU.TIM2CR1, x - {5,6});
			(* disable buffering of ARR register *)
				SYSTEM.GET(MCU.TIM2CR1, x);
				SYSTEM.PUT(MCU.TIM2CR1, x - {ARPE});
			SYSTEM.PUT(MCU.TIM2ARR, tFrame - 2);
			(* clock division: tDTS := tCKINT *)
				SYSTEM.GET(MCU.TIM2CR1, x);
				SYSTEM.PUT(MCU.TIM2CR1, x - {8,9});
			(* direction: upcounter *)
				SYSTEM.GET(MCU.TIM2CR1, x);
				SYSTEM.PUT(MCU.TIM2CR1, x - {DIR});
			(* disable one pulse mode *)
				SYSTEM.GET(MCU.TIM2CR1, x);
				SYSTEM.PUT(MCU.TIM2CR1, x - {OPM});
			(* update request source: overflow/underflow, UG, slave *)
				SYSTEM.GET(MCU.TIM2CR1, x);
				SYSTEM.PUT(MCU.TIM2CR1, x - {URS});
			(* UEV *)
				SYSTEM.GET(MCU.TIM2CR1, x);
				SYSTEM.PUT(MCU.TIM2CR1, x + {UDIS});
		
			SYSTEM.PUT(MCU.TIM2CR2, {});
			SYSTEM.PUT(MCU.TIM2SMCR, {});
			SYSTEM.PUT(MCU.TIM2CCMR1, {});
			SYSTEM.PUT(MCU.TIM2CCMR2, {});
		
			(* setup DMA-related registers *)
				SYSTEM.PUT(MCU.TIM2DCR, {});
				SYSTEM.PUT(MCU.TIM2DMAR, 0);
		
		SetTIM2(0, positions DIV 2);
		SetTIM2(1, positions DIV 2);
		SetTIM2(2, positions DIV 2);
		SetTIM2(3, positions DIV 2);
	
		(* PWM mode 1, enable CCRx preloading *)
			SYSTEM.PUT(MCU.TIM2CCMR1, {OC1PE,5,6,OC2PE,8+5,8+6});
			SYSTEM.PUT(MCU.TIM2CCMR2, {OC3PE,5,6,OC4PE,8+5,8+6});
	
		(* enable compare outputs *)
			SYSTEM.PUT(MCU.TIM2CCER, {CC1E,CC2E,CC3E,CC4E} + polarity);
	
		(* enable timer counter  *)
			SYSTEM.GET(MCU.TIM2CR1, x);
			SYSTEM.PUT(MCU.TIM2CR1, x + {CEN});
		
	END InitTIM2;
	
	
	PROCEDURE SetTIM5* (chn: INTEGER; pos: INTEGER);
	BEGIN
		ASSERT(chn >= 0);
		ASSERT(chn < 4);
		ASSERT(pos >= 0);
		ASSERT(pos < positions);
		SYSTEM.PUT(MCU.TIM5CCR1 + 4 * chn, tSigMin0 + pos)
	END SetTIM5;

	PROCEDURE InitTIM5*(period, prescaler: INTEGER);
	
		CONST
				(* 80000000 / period / prescaler = 64000 < 2^32,
					2^16 = 65536
					2^32 = 4294967296
				*)

			(* NVIC *)
				ISER = ARMv7M.NVICISER0 + (MCU.TIM5Int DIV 32) * 4;
				ICER = ARMv7M.NVICICER0 + (MCU.TIM5Int DIV 32) * 4;
				int = MCU.TIM5Int MOD 32;
				
			(* RCCAPB1[LP]ENR bits: *)
				TIM5EN = 3;
			(* TIM1CR1 bits: *)
				CEN = 0;
				UDIS = 1;
				URS = 2;
				OPM = 3;
				DIR = 4;
				ARPE = 7;
			(* TIM1DIER bits: *)
				UIE = 0;
				CC1IE = 1; CC2IE = 2; CC3IE = 3; CC4IE = 4;
				COMIE = 5;
				TIE = 6;
				BIE = 7;
			(* TIM1CCER bits: *)
				CC1E = 0; CC1P = 1;
				CC2E = 4; CC2P = 5;
				CC3E = 8; CC3P = 9;
				CC4E = 12; CC4P = 13;
			direct = {}; inverse = {CC1P,CC2P,CC3P,CC4P}; polarity = direct;
			(* TIM1CCMR1 bits: *)
				OC1FE = 2; OC1PE = 3; OC1CE = 7;
				OC2FE = 8 + 2; OC2PE = 8 + 3; OC2CE = 8 + 7;
			(* TIM1CCMR2 bits: *)
				OC3FE = 2; OC3PE = 3; OC3CE = 7;
				OC4FE = 8 + 2; OC4PE = 8 + 3; OC4CE = 8 + 7;

		VAR x: SET; tFrame: INTEGER;
		
	BEGIN
		
		(* disable interrupts *)
			SYSTEM.PUT(ICER, {int}); ARMv7M.ISB;
			SYSTEM.PUT(MCU.TIM5DIER, {});
			
		(* NOTE: timer frequency = 2 * APB1 frequency *)
			tFrame := Sys.TIMCLK1 DIV ( period * prescaler);
			positions := Sys.TIMCLK1 DIV ( period * prescaler);
			tSigMin0 := 0;
			
			(*
			positions := (3 * Sys.PCLK1) DIV (divider * 1250); (* 1.2 msec *)
			tSigMin0 := (9 * Sys.PCLK1) DIV (divider * 5000); (* 900 usec *)
			*)
			
		(* disable timer counter *)
			SYSTEM.GET(MCU.TIM5CR1, x);
			SYSTEM.PUT(MCU.TIM5CR1, x - {CEN});
		
		(* disable capture/compare channels *)
			SYSTEM.PUT(MCU.TIM5CCER, polarity);

		(* reset timer counter *)
			SYSTEM.PUT(MCU.TIM5CNT, 0);

		(* enable TIM2 clock *)
			SYSTEM.GET(MCU.RCCAPB1ENR, x);
			SYSTEM.PUT(MCU.RCCAPB1ENR, x + {TIM5EN});

			SYSTEM.GET(MCU.RCCAPB1LPENR, x);
			SYSTEM.PUT(MCU.RCCAPB1LPENR, x + {TIM5EN});
		
		(* configure PWM pins *)
			PinCfg.Configure(PinCfg.A, 0,
				PinCfg.alt, PinCfg.pushPull, PinCfg.low, PinCfg.noPull, PinCfg.AF2);
			PinCfg.Configure(PinCfg.A, 1,
				PinCfg.alt, PinCfg.pushPull, PinCfg.low, PinCfg.noPull, PinCfg.AF2);
			PinCfg.Configure(PinCfg.A, 2,
				PinCfg.alt, PinCfg.pushPull, PinCfg.low, PinCfg.noPull, PinCfg.AF2);
			PinCfg.Configure(PinCfg.A, 3,
				PinCfg.alt, PinCfg.pushPull, PinCfg.low, PinCfg.noPull, PinCfg.AF2);
			
		(* timer setup *)
			(* setup prescaler *)
				SYSTEM.PUT(MCU.TIM5PSC, prescaler - 1);
			(* edge-aligned mode *)
				SYSTEM.GET(MCU.TIM5CR1, x);
				SYSTEM.PUT(MCU.TIM5CR1, x - {5,6});
			(* disable buffering of ARR register *)
				SYSTEM.GET(MCU.TIM5CR1, x);
				SYSTEM.PUT(MCU.TIM5CR1, x - {ARPE});
				SYSTEM.PUT(MCU.TIM5ARR, tFrame - 2);
			(* clock division: tDTS := tCKINT *)
				SYSTEM.GET(MCU.TIM5CR1, x);
				SYSTEM.PUT(MCU.TIM5CR1, x - {8,9});
			(* direction: upcounter *)
				SYSTEM.GET(MCU.TIM5CR1, x);
				SYSTEM.PUT(MCU.TIM5CR1, x - {DIR});
			(* disable one pulse mode *)
				SYSTEM.GET(MCU.TIM5CR1, x);
				SYSTEM.PUT(MCU.TIM5CR1, x - {OPM});
			(* update request source: overflow/underflow, UG, slave *)
				SYSTEM.GET(MCU.TIM5CR1, x);
				SYSTEM.PUT(MCU.TIM5CR1, x - {URS});
			(* UEV *)
				SYSTEM.GET(MCU.TIM5CR1, x);
				SYSTEM.PUT(MCU.TIM5CR1, x + {UDIS});
		
			SYSTEM.PUT(MCU.TIM5CR2, {});
			SYSTEM.PUT(MCU.TIM5SMCR, {});
			SYSTEM.PUT(MCU.TIM5CCMR1, {});
			SYSTEM.PUT(MCU.TIM5CCMR2, {});
		
			(* setup DMA-related registers *)
				SYSTEM.PUT(MCU.TIM5DCR, {});
				SYSTEM.PUT(MCU.TIM5DMAR, 0);
		
			SetTIM5(0, positions DIV 2);
			SetTIM5(1, positions DIV 2);
			SetTIM5(2, positions DIV 2);
			SetTIM5(3, positions DIV 2);
			
		(* PWM mode 1, enable CCRx preloading *)
			SYSTEM.PUT(MCU.TIM5CCMR1, {OC1PE,5,6,OC2PE,8+5,8+6});
			SYSTEM.PUT(MCU.TIM5CCMR2, {OC3PE,5,6,OC4PE,8+5,8+6});
	
		(* enable compare outputs *)
			SYSTEM.PUT(MCU.TIM5CCER, {CC1E,CC2E,CC3E,CC4E} + polarity);
	
		(* enable timer counter  *)
			SYSTEM.GET(MCU.TIM5CR1, x);
			SYSTEM.PUT(MCU.TIM5CR1, x + {CEN});
		
		
	END InitTIM5;
	

		
END PWM.