From 6750b206a812d79a3843d0781a4561b204926e72 Mon Sep 17 00:00:00 2001 From: Andrii Fil Date: Thu, 10 Apr 2025 23:58:06 +0300 Subject: [PATCH 1/3] Append support DMA for PWM. Create example demo_pwm_dma_continuous --- .../demo_dma_mem_to_peripheral_pwm/README.md | 5 + .../demo_pwm_dma_continuous.gpr | 18 +++ .../demo_dma_mem_to_peripheral_pwm/gnat.adc | 2 + .../src/demo_pwm_dma_continuous.adb | 126 ++++++++++++++++++ arch/ARM/STM32/drivers/stm32-pwm.adb | 48 ++++--- arch/ARM/STM32/drivers/stm32-pwm.ads | 8 ++ 6 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/README.md create mode 100644 arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/demo_pwm_dma_continuous.gpr create mode 100644 arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/gnat.adc create mode 100644 arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/src/demo_pwm_dma_continuous.adb diff --git a/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/README.md b/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/README.md new file mode 100644 index 000000000..302a66406 --- /dev/null +++ b/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/README.md @@ -0,0 +1,5 @@ +This program demonstrates using the DMA controller to send data from +memory to a peripheral. + +Specifically, it uses DMA to send a duty cycles values to the Timer (PWM). + diff --git a/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/demo_pwm_dma_continuous.gpr b/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/demo_pwm_dma_continuous.gpr new file mode 100644 index 000000000..77ab160f2 --- /dev/null +++ b/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/demo_pwm_dma_continuous.gpr @@ -0,0 +1,18 @@ +with "../../../../../boards/stm32f4xx_m/stm32f4xx_m_full.gpr"; + +project Demo_PWM_DMA_Continuous extends "../../../../../examples/shared/common/common.gpr" is + + for Main use ("demo_pwm_dma_continuous.adb"); + for Languages use ("Ada"); + for Source_Dirs use ("src"); + for Object_Dir use "obj/" & STM32F4XX_M_Full.Build; + for Runtime ("Ada") use STM32F4XX_M_Full'Runtime("Ada"); + for Create_Missing_Dirs use "true"; + + package Builder is + for Global_Configuration_Pragmas use "gnat.adc"; + end Builder; + + package Compiler renames STM32F4XX_M_Full.Compiler; + +end Demo_PWM_DMA_Continuous; diff --git a/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/gnat.adc b/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/gnat.adc new file mode 100644 index 000000000..ba680210b --- /dev/null +++ b/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/gnat.adc @@ -0,0 +1,2 @@ +pragma Partition_Elaboration_Policy (Sequential); + diff --git a/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/src/demo_pwm_dma_continuous.adb b/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/src/demo_pwm_dma_continuous.adb new file mode 100644 index 000000000..22576f8f5 --- /dev/null +++ b/arch/ARM/STM32/driver_demos/demo_dma_mem_to_peripheral_pwm/src/demo_pwm_dma_continuous.adb @@ -0,0 +1,126 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2025, AdaCore -- +-- -- +-- 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 the copyright holder 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. -- +-- -- +------------------------------------------------------------------------------ + +with STM32.Board; +with STM32.Device; +with STM32.PWM; +with STM32.Timers; +with STM32.DMA; +with HAL; + +procedure Demo_PWM_DMA_Continuous is + type Data is array (0 .. 200) of HAL.UInt32; + for Data'Component_Size use 32; + Bytes_To_Transfer : constant := Data'Length; + Source_Block : Data; + + Controller : STM32.DMA.DMA_Controller renames STM32.Device.DMA_1; + Configuration : STM32.DMA.DMA_Stream_Configuration; + + Tx_Channel : constant STM32.DMA.DMA_Channel_Selector := STM32.DMA.Channel_6; + Tx_Stream : constant STM32.DMA.DMA_Stream_Selector := STM32.DMA.Stream_0; + + -- See RM0090, section 10.3.3, for the DMA channel request mapping tables + -- that say which controllers, and which channels and streams on those + -- controllers, can connect to which devices. For example, it is channel + -- six and stream null that connect DMA1 to the timer of TIM5_UP, so + -- we specify those values above. + + Selected_Timer : STM32.Timers.Timer renames STM32.Device.Timer_5; + Timer_AF : constant STM32.GPIO_Alternate_Function := + STM32.Device.GPIO_AF_TIM5_2; + + Output_Channel : constant STM32.Timers.Timer_Channel := + STM32.Timers.Channel_2; + + Requested_Frequency : constant STM32.PWM.Hertz := 100; + + LED_Control : STM32.PWM.PWM_Modulator; + +begin + + STM32.PWM.Configure_PWM_Timer (Selected_Timer'Access, Requested_Frequency); + + LED_Control.Attach_PWM_Channel + (Selected_Timer'Access, + Output_Channel, + STM32.Board.Green_LED, + Timer_AF); + LED_Control.Enable_Output; + + -- Initialize of values + for Ind in 0 .. 100 loop + Source_Block (Ind) := LED_Control.Calculate_Compare_Value (Ind); + Source_Block (Data'Last - Ind) := Source_Block (Ind); + end loop; + + STM32.Timers.Set_Output_Preload_Enable + (Selected_Timer, Output_Channel, True); + + STM32.Timers.Configure_DMA (Selected_Timer, + STM32.Timers.DMA_Base_CCR2, + STM32.Timers.DMA_Burst_Length_1); + + STM32.Timers.Enable_DMA_Source + (Selected_Timer, STM32.Timers.Timer_DMA_Update); + + STM32.Device.Enable_Clock (Controller); + + STM32.DMA.Reset (Controller, Tx_Stream); + + Configuration.Channel := Tx_Channel; + Configuration.Direction := STM32.DMA.Memory_To_Peripheral; + Configuration.Increment_Peripheral_Address := False; + Configuration.Increment_Memory_Address := True; + Configuration.Peripheral_Data_Format := STM32.DMA.Words; + Configuration.Memory_Data_Format := STM32.DMA.Words; + Configuration.Operation_Mode := STM32.DMA.Circular_Mode; + Configuration.Priority := STM32.DMA.Priority_Very_High; + Configuration.FIFO_Enabled := False; + + STM32.DMA.Configure (Controller, Tx_Stream, Configuration); + + STM32.DMA.Start_Transfer + (Controller, + Tx_Stream, + Source => Source_Block'Address, + Destination => STM32.PWM.Data_Register_Address (LED_Control), + Data_Count => Bytes_To_Transfer); + + STM32.Timers.Enable_Capture_Compare_DMA (Selected_Timer); + + loop + null; + end loop; +end Demo_PWM_DMA_Continuous; + + + diff --git a/arch/ARM/STM32/drivers/stm32-pwm.adb b/arch/ARM/STM32/drivers/stm32-pwm.adb index ba9877205..1cc13ad2b 100644 --- a/arch/ARM/STM32/drivers/stm32-pwm.adb +++ b/arch/ARM/STM32/drivers/stm32-pwm.adb @@ -29,9 +29,9 @@ -- -- ------------------------------------------------------------------------------ -with System; use System; with STM32_SVD; use STM32_SVD; with STM32.Device; use STM32.Device; +with System.Storage_Elements; use System.Storage_Elements; package body STM32.PWM is @@ -60,6 +60,23 @@ package body STM32.PWM is function Has_APB1_Frequency (This : Timer) return Boolean; -- timers 3, 4, 6, 7, 12, 13, 14 + ----------------------------- + -- Calculate_Compare_Value -- + ----------------------------- + + function Calculate_Compare_Value + (This : in out PWM_Modulator; Value : Percentage) return UInt32 is + Period : constant UInt32 := Timer_Period (This); + begin + if Value = 0 then + return 0; + elsif Period < 42949672 then + return ((Period + 1) * UInt32 (Value) / 100) - 1; + else + return ((Period + 1) / 100 * UInt32 (Value)) - 1; + end if; + end Calculate_Compare_Value; + -------------------- -- Set_Duty_Cycle -- -------------------- @@ -68,25 +85,16 @@ package body STM32.PWM is (This : in out PWM_Modulator; Value : Percentage) is - Pulse16 : UInt16; - Pulse32 : UInt32; + Pulse32 : constant UInt32 := Calculate_Compare_Value (This, Value); begin This.Duty_Cycle := Value; - if Value = 0 then - Set_Compare_Value (This.Generator.all, This.Channel, UInt16'(0)); + if Has_32bit_CC_Values (This.Generator.all) then + Set_Compare_Value (This.Generator.all, This.Channel, Pulse32); else - -- for a Value of 0, the computation of Pulse wraps around, so we - -- only compute it when not zero - - if Has_32bit_CC_Values (This.Generator.all) then - Pulse32 := UInt32 ((Timer_Period (This) + 1) * UInt32 (Value) / 100) - 1; - Set_Compare_Value (This.Generator.all, This.Channel, Pulse32); - else - Pulse16 := UInt16 ((Timer_Period (This) + 1) * UInt32 (Value) / 100) - 1; - Set_Compare_Value (This.Generator.all, This.Channel, Pulse16); - end if; + Set_Compare_Value (This.Generator.all, This.Channel, UInt16 (Pulse32)); end if; + end Set_Duty_Cycle; ------------------- @@ -438,4 +446,14 @@ package body STM32.PWM is This'Address = STM32_SVD.TIM13_Base or This'Address = STM32_SVD.TIM14_Base); + --------------------------- + -- Data_Register_Address -- + --------------------------- + + function Data_Register_Address + (This : PWM_Modulator) return Address is + begin + return This.Generator.all'Address + 16#4C#; + end Data_Register_Address; + end STM32.PWM; diff --git a/arch/ARM/STM32/drivers/stm32-pwm.ads b/arch/ARM/STM32/drivers/stm32-pwm.ads index f8a506f2f..d3ddcbd46 100644 --- a/arch/ARM/STM32/drivers/stm32-pwm.ads +++ b/arch/ARM/STM32/drivers/stm32-pwm.ads @@ -62,6 +62,7 @@ with STM32.GPIO; use STM32.GPIO; with STM32.Timers; use STM32.Timers; +with System; use System; package STM32.PWM is pragma Elaborate_Body; @@ -148,6 +149,9 @@ package STM32.PWM is subtype Percentage is Integer range 0 .. 100; + function Calculate_Compare_Value + (This : in out PWM_Modulator; Value : Percentage) return UInt32; + procedure Set_Duty_Cycle (This : in out PWM_Modulator; Value : Percentage) @@ -190,6 +194,10 @@ package STM32.PWM is Polarity : in Timer_Output_Compare_Polarity); -- Set the polarity of the complimentary output of This modulator. + function Data_Register_Address + (This : PWM_Modulator) return Address with Inline; + -- Returns the address of the Timer DMAR Register. + Invalid_Request : exception; -- Raised when the requested frequency is too high or too low for the given -- timer and system clocks when calling Configure_PWM_Timer, or when From 5ff65dfc5ad138ed7494dc6dfe5e7b3859ade9d4 Mon Sep 17 00:00:00 2001 From: Andrii Fil Date: Sat, 12 Apr 2025 22:28:01 +0300 Subject: [PATCH 2/3] Data_Register_Address for Timers --- arch/ARM/STM32/drivers/stm32-pwm.adb | 3 +-- arch/ARM/STM32/drivers/stm32-timers.adb | 10 ++++++++++ arch/ARM/STM32/drivers/stm32-timers.ads | 3 +++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/arch/ARM/STM32/drivers/stm32-pwm.adb b/arch/ARM/STM32/drivers/stm32-pwm.adb index 1cc13ad2b..45993228e 100644 --- a/arch/ARM/STM32/drivers/stm32-pwm.adb +++ b/arch/ARM/STM32/drivers/stm32-pwm.adb @@ -31,7 +31,6 @@ with STM32_SVD; use STM32_SVD; with STM32.Device; use STM32.Device; -with System.Storage_Elements; use System.Storage_Elements; package body STM32.PWM is @@ -453,7 +452,7 @@ package body STM32.PWM is function Data_Register_Address (This : PWM_Modulator) return Address is begin - return This.Generator.all'Address + 16#4C#; + return STM32.Timers.Data_Register_Address (This.Generator.all); end Data_Register_Address; end STM32.PWM; diff --git a/arch/ARM/STM32/drivers/stm32-timers.adb b/arch/ARM/STM32/drivers/stm32-timers.adb index 9d58cd4b7..e4dfc0401 100644 --- a/arch/ARM/STM32/drivers/stm32-timers.adb +++ b/arch/ARM/STM32/drivers/stm32-timers.adb @@ -435,6 +435,16 @@ package body STM32.Timers is This.CR2.Capture_Compare_DMA_Selection := False; end Disable_Capture_Compare_DMA; + --------------------------- + -- Data_Register_Address -- + --------------------------- + + function Data_Register_Address + (This : Timer) return Address is + begin + return This.DMAR'Address; + end Data_Register_Address; + ----------------------- -- Current_Prescaler -- ----------------------- diff --git a/arch/ARM/STM32/drivers/stm32-timers.ads b/arch/ARM/STM32/drivers/stm32-timers.ads index 6e81c1fa0..89095461a 100644 --- a/arch/ARM/STM32/drivers/stm32-timers.ads +++ b/arch/ARM/STM32/drivers/stm32-timers.ads @@ -437,6 +437,9 @@ package STM32.Timers is procedure Disable_Capture_Compare_DMA (This : in out Timer); + function Data_Register_Address + (This : Timer) return Address with Inline; + ---------------------------------------------------------------------------- -- Output Compare Management ---------------------------------------------- From b097715309bdb1f0d99a79f233543f45a70aff29 Mon Sep 17 00:00:00 2001 From: Andrii Fil Date: Sat, 12 Apr 2025 22:32:07 +0300 Subject: [PATCH 3/3] Data_Register_Address remove prefix --- arch/ARM/STM32/drivers/stm32-pwm.adb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/ARM/STM32/drivers/stm32-pwm.adb b/arch/ARM/STM32/drivers/stm32-pwm.adb index 45993228e..24dc0503f 100644 --- a/arch/ARM/STM32/drivers/stm32-pwm.adb +++ b/arch/ARM/STM32/drivers/stm32-pwm.adb @@ -452,7 +452,7 @@ package body STM32.PWM is function Data_Register_Address (This : PWM_Modulator) return Address is begin - return STM32.Timers.Data_Register_Address (This.Generator.all); + return Data_Register_Address (This.Generator.all); end Data_Register_Address; end STM32.PWM;