Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cfefe1f
adding/revising WM8994 driver and Audio wrapper driver for it
Mar 27, 2024
7e0024e
use new IO package for WM8994, with constants for register addresses
Mar 27, 2024
c933f7a
better name for procedure Play
Mar 28, 2024
5701668
Move to signed 16-bit PCM samples, rather than unsigned
Mar 28, 2024
69ca86e
Add important comments re: mystery code and update copyright year
Mar 28, 2024
2e6117e
Make the output device type use a static predicate to limit the options
Mar 28, 2024
4caa595
Change type names to match WM8994 datasheet.
pat-rogers Mar 21, 2026
473f1d4
use new name for WM8994_Audio_Device
pat-rogers Mar 23, 2026
9ad19bd
change procedure name Start_Playing back to Play
pat-rogers Mar 23, 2026
0bb0d7f
use a "private with" for Ravenscar_Time
pat-rogers Mar 24, 2026
582ce16
Minor name refactoring for consistency within the package
pat-rogers May 8, 2026
4ac5b51
Move the odd package-level Boolean objects to be components of the pr…
pat-rogers May 8, 2026
e57ed5e
Update for type name change in package WM8994, in which Analog_Output…
pat-rogers May 9, 2026
f58ee78
Remove immediate internal read after write in procedure I2C_Write.
pat-rogers May 13, 2026
b3f13a3
Keep track of which output is selected and only manipulate the hardwa…
pat-rogers May 13, 2026
869fe0c
Refactor sequences of I2C writes into internal help er procedures for…
pat-rogers May 13, 2026
ca40e5d
Minor formatting and formal parameter name changes
pat-rogers May 13, 2026
d0e4e90
Add ability to specify the incoming sample bit width
pat-rogers May 13, 2026
7192319
Update due to new WM889 CODEC ability to set sample bit width.
pat-rogers May 13, 2026
c25011b
No need for Audio_Device to be aliased
pat-rogers May 14, 2026
b78d315
In Audio.Set_Audio_Clock, add support for missing frequencies
pat-rogers May 14, 2026
95ac63d
Updates for sake of changes to WM8994 package.
pat-rogers May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 153 additions & 101 deletions boards/stm32_common/stm32f746disco/audio.adb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
------------------------------------------------------------------------------
-- --
-- Copyright (C) 2016, AdaCore --
-- Copyright (C) 2016-2026, AdaCore --
-- --
-- Redistribution and use in source and binary forms, with or without --
-- modification, are permitted provided that the following conditions are --
Expand Down Expand Up @@ -32,7 +32,6 @@
-- @author MCD Application Team --
------------------------------------------------------------------------------

with HAL; use HAL;
with STM32; use STM32;
with STM32.Device; use STM32.Device;
with STM32.Board; use STM32.Board;
Expand All @@ -50,25 +49,107 @@ package body Audio is
SAI2_SD_B : GPIO_Point renames PG10;
SAI2_FS_A : GPIO_Point renames PI7;
SAI_Pins : constant GPIO_Points :=
(SAI2_MCLK_A, SAI2_SCK_A, SAI2_SD_A, SAI2_SD_B,
SAI2_FS_A);
(SAI2_MCLK_A, SAI2_SCK_A, SAI2_SD_A, SAI2_SD_B, SAI2_FS_A);
SAI_Pins_AF : GPIO_Alternate_Function renames GPIO_AF_SAI2_10;

-- SAI in/out conf
SAI_Out_Block : SAI_Block renames Block_A;
-- SAI_In_Block : SAI_Block renames Block_B;

procedure Set_Audio_Clock (Freq : Audio_Frequency);

procedure Initialize_Audio_Out_Pins;
procedure Initialize_SAI_Out (Freq : Audio_Frequency);

procedure Initialize_Audio_DMA;
-- Configure the DMA channel to the SAI peripheral

procedure Initialize_SAI_Out (Freq : Audio_Frequency; Sink : Audio_Outputs);

procedure Initialize_Audio_I2C;
-- Initialize the I2C Port to send commands to the driver

function As_Device_Volume (Input : Audio_Volume) return WM8994.Volume_Level;
-- Converts the percentage input value to the device-specific range

----------------
-- Initialize --
----------------

procedure Initialize
(This : in out WM8994_Audio_CODEC;
Volume : Audio_Volume;
Frequency : Audio_Frequency;
Bit_Width : Audio_Bit_Width;
Sink : Audio_Outputs)
is
begin
STM32.SAI.Deinitialize (Audio_SAI, SAI_Out_Block);

Set_Audio_Clock (Frequency);

Initialize_Audio_Out_Pins;
Initialize_Audio_DMA;
Initialize_SAI_Out (Frequency, Sink);
Initialize_Audio_I2C;

if This.Device.Chip_ID /= WM8994.WM8994_ID then
raise Program_Error with "Invalid Chip ID received from the Audio Codec";
end if;

This.Device.Reset;
This.Device.Initialize
(Input => WM8994.No_Input,
Output => WM8994.Output_Device (Sink),
Volume => As_Device_Volume (Volume),
Frequency => WM8994.Audio_Frequency (Frequency),
Bit_Width => WM8994.Audio_Sample_Width (Bit_Width));

This.Sink := WM8994.Output_Device (Sink);
-- For sake of any individual calls to Set_Frequency, assuming the STM
-- code is correct that we also need to set the clocks when setting the
-- frequency
end Initialize;

----------------
-- Set_Volume --
----------------

procedure Set_Volume
(This : in out WM8994_Audio_CODEC;
Volume : Audio_Volume)
is
begin
This.Device.Set_Volume (As_Device_Volume (Volume));
end Set_Volume;

-------------------
-- Set_Frequency --
-------------------

procedure Set_Frequency
(This : in out WM8994_Audio_CODEC;
Frequency : Audio_Frequency)
is
use type WM8994.Output_Device;
begin
if This.Sink = WM8994.No_Output then
raise Constraint_Error with "No prior call to Initialize";
end if;

-- The following is per the example source file from STM,
-- STM32746G-Disco_example\board_drivers\stm32746g_discovery_audio.c,
-- whereas the WM8894 driver just has a specific function that writes
-- the necessary configuration bits to the device register without any
-- clock configuration too.
Set_Audio_Clock (Frequency);
STM32.SAI.Disable (Audio_SAI, SAI_Out_Block);
Initialize_SAI_Out (Frequency, Audio_Outputs (This.Sink));
STM32.SAI.Enable (Audio_SAI, SAI_Out_Block);
end Set_Frequency;

---------------------
-- Set_Audio_Clock --
---------------------

procedure Set_Audio_Clock (Freq : Audio_Frequency)
is
procedure Set_Audio_Clock (Freq : Audio_Frequency) is
begin
-- Two groups of frequencies: the 44kHz family and the 48kHz family
-- The Actual audio frequency is calculated then with the following
Expand All @@ -86,37 +167,32 @@ package body Audio is
PLLI2SQ => 2, -- SAI Clk(First level) = 214.5 MHz
PLLI2SDIVQ => 19); -- I2S Clk = 215.4 / 19 = 11.289 MHz

when Audio_Freq_8kHz | Audio_Freq_16kHz |
Audio_Freq_48kHz | Audio_Freq_96kHz =>
when Audio_Freq_8kHz | Audio_Freq_12kHz | Audio_Freq_16kHz |
Audio_Freq_24kHz | Audio_Freq_48kHz | Audio_Freq_96kHz =>
Configure_SAI_I2S_Clock
(Audio_SAI,
PLLI2SN => 344, -- VCO Output = 344MHz
PLLI2SQ => 7, -- SAI Clk(First level) = 49.142 MHz
PLLI2SDIVQ => 1); -- I2S Clk = 49.142 MHz
PLLI2SDIVQ => 1); -- I2S Clk = 49.142 MHz

when Audio_Freq_88kHz =>
-- Best rational approx of 88200*256 Hz = 22.5792 MHz from 1 MHz VCO;
-- 271/4/3 = 22.583 MHz, MCKDIV=0 -> FS = 88216 Hz (0.018% error)
Configure_SAI_I2S_Clock
(Audio_SAI,
PLLI2SN => 271, -- VCO Output = 271MHz
PLLI2SQ => 4, -- SAI Clk(First level) = 67.75 MHz
PLLI2SDIVQ => 3); -- SAI Clk = 22.583 MHz
end case;
end Set_Audio_Clock;

-------------------------------
-- Initialize_Audio_Out_Pins --
-------------------------------
--------------------------
-- Initialize_Audio_DMA --
--------------------------

procedure Initialize_Audio_Out_Pins
is
procedure Initialize_Audio_DMA is
begin
Enable_Clock (Audio_SAI);
Enable_Clock (SAI_Pins);

Configure_IO
(SAI_Pins,
(Mode => Mode_AF,
AF => SAI_Pins_AF,
AF_Output_Type => Push_Pull,
AF_Speed => Speed_High,
Resistors => Floating));

Enable_Clock (Audio_DMA);

-- Configure the DMA channel to the SAI peripheral
Disable (Audio_DMA, Audio_DMA_Out_Stream);
Configure
(Audio_DMA,
Expand All @@ -134,16 +210,38 @@ package body Audio is
Memory_Burst_Size => Memory_Burst_Single,
Peripheral_Burst_Size => Peripheral_Burst_Single));
Clear_All_Status (Audio_DMA, Audio_DMA_Out_Stream);
end Initialize_Audio_DMA;

-------------------------------
-- Initialize_Audio_Out_Pins --
-------------------------------

procedure Initialize_Audio_Out_Pins is
begin
Enable_Clock (Audio_SAI);
Enable_Clock (SAI_Pins);

Configure_IO
(SAI_Pins,
(Mode => Mode_AF,
AF => SAI_Pins_AF,
AF_Output_Type => Push_Pull,
AF_Speed => Speed_High,
Resistors => Floating));
end Initialize_Audio_Out_Pins;

------------------------
-- Initialize_SAI_Out --
------------------------

procedure Initialize_SAI_Out (Freq : Audio_Frequency)
procedure Initialize_SAI_Out
(Freq : Audio_Frequency;
Sink : Audio_Outputs)
is
Active_Slots : SAI_Slots;
begin
STM32.SAI.Disable (Audio_SAI, SAI_Out_Block);

STM32.SAI.Configure_Audio_Block
(Audio_SAI,
SAI_Out_Block,
Expand All @@ -158,6 +256,7 @@ package body Audio is
Synchronization => Asynchronous_Mode,
Output_Drive => Drive_Immediate,
FIFO_Threshold => FIFO_1_Quarter_Full);

STM32.SAI.Configure_Block_Frame
(Audio_SAI,
SAI_Out_Block,
Expand All @@ -166,22 +265,32 @@ package body Audio is
Frame_Sync => FS_Frame_And_Channel_Identification,
FS_Polarity => FS_Active_Low,
FS_Offset => Before_First_Bit);

case Sink is
when Headphone =>
Active_Slots := Slot_0 or Slot_2;
when Speaker =>
Active_Slots := Slot_1 or Slot_3;
when Both =>
Active_Slots := Slot_0 or Slot_1 or Slot_2 or Slot_3;
end case;

STM32.SAI.Configure_Block_Slot
(Audio_SAI,
SAI_Out_Block,
First_Bit_Offset => 0,
Slot_Size => Data_Size,
Number_Of_Slots => 4,
Enabled_Slots => Slot_0 or Slot_2);
Enabled_Slots => Active_Slots);

STM32.SAI.Enable (Audio_SAI, SAI_Out_Block);
end Initialize_SAI_Out;

--------------------------
-- Initialize_Audio_I2C --
--------------------------

procedure Initialize_Audio_I2C
is
procedure Initialize_Audio_I2C is
begin
STM32.Setup.Setup_I2C_Master (Port => Audio_I2C,
SDA => Audio_I2C_SDA,
Expand All @@ -191,47 +300,12 @@ package body Audio is
Clock_Speed => 100_000);
end Initialize_Audio_I2C;

----------------
-- Initialize --
----------------

procedure Initialize_Audio_Out
(This : in out WM8994_Audio_Device;
Volume : Audio_Volume;
Frequency : Audio_Frequency)
is
begin
STM32.SAI.Deinitialize (Audio_SAI, SAI_Out_Block);

Set_Audio_Clock (Frequency);

-- Initialize the SAI
Initialize_Audio_Out_Pins;
Initialize_SAI_Out (Frequency);

-- Initialize the I2C Port to send commands to the driver
Initialize_Audio_I2C;

if This.Device.Read_ID /= WM8994.WM8994_ID then
raise Constraint_Error with "Invalid ID received from the Audio Code";
end if;

This.Device.Reset;
This.Device.Init
(Input => WM8994.No_Input,
Output => WM8994.Auto,
Volume => UInt8 (Volume),
Frequency =>
WM8994.Audio_Frequency'Enum_Val
(Audio_Frequency'Enum_Rep (Frequency)));
end Initialize_Audio_Out;

----------
-- Play --
----------

procedure Play
(This : in out WM8994_Audio_Device;
(This : in out WM8994_Audio_CODEC;
Buffer : Audio_Buffer)
is
begin
Expand All @@ -258,7 +332,7 @@ package body Audio is
-- Pause --
-----------

procedure Pause (This : in out WM8994_Audio_Device) is
procedure Pause (This : in out WM8994_Audio_CODEC) is
begin
This.Device.Pause;
DMA_Pause (Audio_SAI, SAI_Out_Block);
Expand All @@ -268,8 +342,7 @@ package body Audio is
-- Resume --
------------

procedure Resume (This : in out WM8994_Audio_Device)
is
procedure Resume (This : in out WM8994_Audio_CODEC) is
begin
This.Device.Resume;
DMA_Resume (Audio_SAI, SAI_Out_Block);
Expand All @@ -279,8 +352,7 @@ package body Audio is
-- Stop --
----------

procedure Stop (This : in out WM8994_Audio_Device)
is
procedure Stop (This : in out WM8994_Audio_CODEC) is
begin
This.Device.Stop (WM8994.Stop_Power_Down_Sw);
DMA_Stop (Audio_SAI, SAI_Out_Block);
Expand All @@ -289,32 +361,12 @@ package body Audio is
STM32.DMA.Clear_All_Status (Audio_DMA, Audio_DMA_Out_Stream);
end Stop;

----------------
-- Set_Volume --
----------------

procedure Set_Volume
(This : in out WM8994_Audio_Device;
Volume : Audio_Volume)
is
begin
This.Device.Set_Volume (UInt8 (Volume));
end Set_Volume;

-------------------
-- Set_Frequency --
-------------------
----------------------
-- As_Device_Volume --
----------------------

procedure Set_Frequency
(This : in out WM8994_Audio_Device;
Frequency : Audio_Frequency)
is
pragma Unreferenced (This);
begin
Set_Audio_Clock (Frequency);
STM32.SAI.Disable (Audio_SAI, SAI_Out_Block);
Initialize_SAI_Out (Frequency);
STM32.SAI.Enable (Audio_SAI, SAI_Out_Block);
end Set_Frequency;
function As_Device_Volume (Input : Audio_Volume) return WM8994.Volume_Level is
(if Input = 100 then WM8994.Max_Volume
else UInt16 (Input) * WM8994.Max_Volume / 100);

end Audio;
Loading
Loading