diff --git a/src/devices/RP2040/rp-device.ads b/src/devices/RP2040/rp-device.ads index a8fd0f4..6529d53 100644 --- a/src/devices/RP2040/rp-device.ads +++ b/src/devices/RP2040/rp-device.ads @@ -1,5 +1,5 @@ -- --- Copyright 2021 (C) Jeremy Grosser +-- Copyright 2021-2024 (C) Jeremy Grosser -- -- SPDX-License-Identifier: BSD-3-Clause -- @@ -23,15 +23,14 @@ package RP.Device is SPI_0 : aliased RP.SPI.SPI_Port (0, RP2040_SVD.SPI.SPI0_Periph'Access); SPI_1 : aliased RP.SPI.SPI_Port (1, RP2040_SVD.SPI.SPI1_Periph'Access); - -- RP.I2C implements the low level interface and is recommended if you need - -- precise control over timing or repeated start conditions. + -- RP.I2C is a lower level implementation of the I2C driver that exposes + -- more diagnostic information. It is not recommended to be used in + -- production code. Use RP.I2C_Master instead. I2C_0 : aliased RP.I2C.I2C_Port (0, RP2040_SVD.I2C.I2C0_Periph'Access); I2C_1 : aliased RP.I2C.I2C_Port (1, RP2040_SVD.I2C.I2C1_Periph'Access); - -- RP.I2C_Master implements the HAL.I2C interface and is recommended if - -- your application needs to be portable to other microcontrollers. - I2CM_0 : aliased RP.I2C_Master.I2C_Master_Port (0, RP2040_SVD.I2C.I2C0_Periph'Access); - I2CM_1 : aliased RP.I2C_Master.I2C_Master_Port (1, RP2040_SVD.I2C.I2C1_Periph'Access); + I2CM_0 : aliased RP.I2C_Master.I2C_Master_Port (0); + I2CM_1 : aliased RP.I2C_Master.I2C_Master_Port (1); UART_0 : aliased RP.UART.UART_Port (0, RP2040_SVD.UART.UART0_Periph'Access); UART_1 : aliased RP.UART.UART_Port (1, RP2040_SVD.UART.UART1_Periph'Access); diff --git a/src/drivers/rp-i2c_master.adb b/src/drivers/rp-i2c_master.adb index 4476887..36ef65c 100644 --- a/src/drivers/rp-i2c_master.adb +++ b/src/drivers/rp-i2c_master.adb @@ -1,84 +1,354 @@ -- --- Copyright 2022 (C) Jeremy Grosser +-- Copyright 2022-2024 (C) Jeremy Grosser -- -- SPDX-License-Identifier: BSD-3-Clause -- -with HAL; use HAL; +with RP.Clock; +with System; package body RP.I2C_Master is - use type RP.I2C.I2C_Status; - use type HAL.I2C.I2C_Status; + type CON_Register is record + STOP_DET_IF_MASTER_ACTIVE : Boolean := False; + RX_FIFO_FULL_HLD_CTRL : Boolean := False; + TX_EMPTY_CTRL : Boolean := False; + STOP_DET_IFADDRESSED : Boolean := False; + IC_SLAVE_DISABLE : Boolean := True; + IC_RESTART_EN : Boolean := True; + IC_10BITADDR_MASTER : Boolean := False; + IC_10BITADDR_SLAVE : Boolean := False; + SPEED : UInt2 := 2; + MASTER_MODE : Boolean := True; + end record + with Volatile_Full_Access, + Effective_Writes, + Async_Readers, + Async_Writers, + Object_Size => 32; + for CON_Register use record + STOP_DET_IF_MASTER_ACTIVE at 0 range 10 .. 10; + RX_FIFO_FULL_HLD_CTRL at 0 range 9 .. 9; + TX_EMPTY_CTRL at 0 range 8 .. 8; + STOP_DET_IFADDRESSED at 0 range 7 .. 7; + IC_SLAVE_DISABLE at 0 range 6 .. 6; + IC_RESTART_EN at 0 range 5 .. 5; + IC_10BITADDR_MASTER at 0 range 4 .. 4; + IC_10BITADDR_SLAVE at 0 range 3 .. 3; + SPEED at 0 range 1 .. 2; + MASTER_MODE at 0 range 0 .. 0; + end record; - function To_HAL_Status - (S : RP.I2C.I2C_Status) - return HAL.I2C.I2C_Status + type TAR_Register is record + SPECIAL : Boolean := False; + GC_OR_START : Boolean := False; + TAR : UInt10 := 16#55#; + end record + with Volatile_Full_Access, + Effective_Writes, + Async_Readers, + Object_Size => 32; + for TAR_Register use record + SPECIAL at 0 range 11 .. 11; + GC_OR_START at 0 range 10 .. 10; + TAR at 0 range 0 .. 9; + end record; + + type DATA_CMD_Register is record + FIRST_DATA_BYTE : Boolean := False; + RESTART : Boolean := False; + STOP : Boolean := False; + CMD : Boolean := False; + DAT : UInt8 := 0; + end record + with Volatile_Full_Access, + Effective_Writes, + Effective_Reads, + Async_Writers, + Async_Readers, + Object_Size => 32; + for DATA_CMD_Register use record + FIRST_DATA_BYTE at 0 range 11 .. 11; + RESTART at 0 range 10 .. 10; + STOP at 0 range 9 .. 9; + CMD at 0 range 8 .. 8; + DAT at 0 range 0 .. 7; + end record; + + type INTR_Register is record + RESTART_DET : Boolean; + GEN_CALL : Boolean; + START_DET : Boolean; + STOP_DET : Boolean; + ACTIVITY : Boolean; + RX_DONE : Boolean; + TX_ABRT : Boolean; + RD_REQ : Boolean; + TX_EMPTY : Boolean; + TX_OVER : Boolean; + RX_FULL : Boolean; + RX_OVER : Boolean; + RX_UNDER : Boolean; + end record + with Volatile_Full_Access, + Async_Writers, + Object_Size => 32; + for INTR_Register use record + RESTART_DET at 0 range 12 .. 12; + GEN_CALL at 0 range 11 .. 11; + START_DET at 0 range 10 .. 10; + STOP_DET at 0 range 9 .. 9; + ACTIVITY at 0 range 8 .. 8; + RX_DONE at 0 range 7 .. 7; + TX_ABRT at 0 range 6 .. 6; + RD_REQ at 0 range 5 .. 5; + TX_EMPTY at 0 range 4 .. 4; + TX_OVER at 0 range 3 .. 3; + RX_FULL at 0 range 2 .. 2; + RX_OVER at 0 range 1 .. 1; + RX_UNDER at 0 range 0 .. 0; + end record; + + type ENABLE_Register is record + TX_CMD_BLOCK : Boolean := False; + TX_ABORT : Boolean := False; + ENABLE : Boolean := False; + end record + with Volatile_Full_Access, + Effective_Writes, + Async_Readers, + Async_Writers, + Object_Size => 32; + for ENABLE_Register use record + TX_CMD_BLOCK at 0 range 2 .. 2; + TX_ABORT at 0 range 1 .. 1; + ENABLE at 0 range 0 .. 0; + end record; + + type SDA_HOLD_Register is record + SDA_RX_HOLD : UInt8 := 16#00#; + SDA_TX_HOLD : UInt16 := 16#0001#; + end record + with Volatile_Full_Access, + Effective_Writes, + Async_Readers, + Object_Size => 32; + for SDA_HOLD_Register use record + SDA_RX_HOLD at 0 range 16 .. 23; + SDA_TX_HOLD at 0 range 0 .. 15; + end record; + + type CLR_Register is mod 2 ** 32 + with Volatile_Full_Access, + Effective_Reads, + Async_Writers, + Async_Readers, + Object_Size => 32; + + type I2C_Peripheral is record + CON : CON_Register; + TAR : TAR_Register; + DATA_CMD : DATA_CMD_Register; + FS_SCL_LCNT : UInt32 := 16#002F#; + FS_SCL_HCNT : UInt32 := 16#0006#; + CLR_TX_ABRT : CLR_Register; + CLR_STOP_DET : CLR_Register; + RAW_INTR_STAT : INTR_Register; + ENABLE : ENABLE_Register; + TXFLR : UInt32; + RXFLR : UInt32; + SDA_HOLD : SDA_HOLD_Register; + TX_ABRT_SOURCE : UInt32; + FS_SPKLEN : UInt32 := 16#07#; + end record + with Volatile; + for I2C_Peripheral use record + CON at 16#00# range 0 .. 31; + TAR at 16#04# range 0 .. 31; + DATA_CMD at 16#10# range 0 .. 31; + FS_SCL_LCNT at 16#18# range 0 .. 31; + FS_SCL_HCNT at 16#1C# range 0 .. 31; + RAW_INTR_STAT at 16#34# range 0 .. 31; + CLR_TX_ABRT at 16#54# range 0 .. 31; + CLR_STOP_DET at 16#60# range 0 .. 31; + ENABLE at 16#6C# range 0 .. 31; + TXFLR at 16#74# range 0 .. 31; + RXFLR at 16#78# range 0 .. 31; + SDA_HOLD at 16#7C# range 0 .. 31; + TX_ABRT_SOURCE at 16#80# range 0 .. 31; + FS_SPKLEN at 16#A0# range 0 .. 31; + end record; + + type Any_I2C_Peripheral is not null access all I2C_Peripheral; + + I2C_0 : aliased I2C_Peripheral + with Import, Address => System'To_Address (16#4004_4000#); + I2C_1 : aliased I2C_Peripheral + with Import, Address => System'To_Address (16#4004_8000#); + + function Periph + (This : I2C_Master_Port) + return Any_I2C_Peripheral + is (case This.Num is + when 0 => I2C_0'Access, + when 1 => I2C_1'Access); + + function Time_Exceeded + (This : I2C_Master_Port) + return Boolean is + use type RP.Timer.Time; begin - case S is - when RP.I2C.Ok => return HAL.I2C.Ok; - when RP.I2C.Timeout => return HAL.I2C.Err_Timeout; - when RP.I2C.Error => return HAL.I2C.Err_Error; - end case; - end To_HAL_Status; + return RP.Timer.Clock >= This.Deadline; + end Time_Exceeded; procedure Configure (This : in out I2C_Master_Port; Baudrate : Hertz; Address_Size : I2C_Address_Size := Address_Size_7b) is + P : constant Any_I2C_Peripheral := Periph (This); + CLK_SYS : constant Hertz := RP.Clock.Frequency (RP.Clock.SYS); + Period : constant Natural := Natural ((CLK_SYS + Baudrate / 2) / Baudrate); + LCNT : constant Natural := Period * 3 / 5; + HCNT : constant Natural := Period - LCNT; + SDA_TX_HOLD : constant Natural := + ((CLK_SYS * 3) / (if Baudrate < 1e6 then 10e6 else 25e6)) + 1; begin + P.ENABLE := (others => False); + P.CON := + (TX_EMPTY_CTRL => True, + IC_10BITADDR_MASTER => (Address_Size = Address_Size_10b), + others => <>); + P.FS_SCL_HCNT := UInt32 (HCNT); + P.FS_SCL_LCNT := UInt32 (LCNT); + P.SDA_HOLD := + (SDA_RX_HOLD => 0, + SDA_TX_HOLD => UInt16 (SDA_TX_HOLD)); + P.ENABLE.ENABLE := True; + This.Restart_On_Next := False; This.Address_Size := Address_Size; - if Baudrate >= 1_000_000 then - This.Port.Configure ((Role => RP.I2C.Controller, Timing => RP.I2C.Fast_Mode_Plus)); - elsif Baudrate >= 400_000 then - This.Port.Configure ((Role => RP.I2C.Controller, Timing => RP.I2C.Fast_Mode_Plus)); - else - This.Port.Configure ((Role => RP.I2C.Controller, Timing => RP.I2C.Standard_Mode)); - end if; + This.Deadline := RP.Timer.Time'First; end Configure; - procedure Set_Address + procedure Set_Deadline (This : in out I2C_Master_Port; - Addr : HAL.I2C.I2C_Address; - Status : out HAL.I2C.I2C_Status; Deadline : RP.Timer.Time) is - use type RP.Timer.Time; - -- Most drivers that use HAL.I2C expect I2C_Address to be shifted left - -- with the LSB set by the controller depending on whether the - -- transaction is a read or write. - -- - -- RP.I2C uses distinct UInt7 and UInt10 types for bus addresses, so we - -- need to shift right first. This doesn't really need to be UInt32, any - -- modular type >2**10 will do, but 32 bits fits nicely in a CPU - -- register. - Bus_Addr : constant UInt32 := Shift_Right (UInt32 (Addr), 1); begin - This.Port.Disable (Deadline); - while This.Port.Enabled loop - if RP.Timer.Clock >= Deadline then - Status := HAL.I2C.Err_Timeout; - return; + This.Deadline := Deadline; + end Set_Deadline; + + procedure Write + (This : in out I2C_Master_Port; + Addr : UInt10; + Data : UInt8_Array; + Error : out Boolean; + Stop : Boolean := True) + is + P : constant Any_I2C_Peripheral := Periph (This); + TX_EMPTY, STOP_DET : Boolean; + TX_ABRT_SOURCE : UInt32; + Timeout, TX_Abort : Boolean := False; + Read_Clear : CLR_Register with Volatile; + begin + P.ENABLE.ENABLE := False; + P.TAR := (TAR => Addr, others => <>); + P.ENABLE.ENABLE := True; + + for I in Data'Range loop + P.DATA_CMD := + (RESTART => (I = Data'First) and then This.Restart_On_Next, + STOP => (I = Data'Last) and then Stop, + DAT => Data (I), + others => <>); + loop + TX_EMPTY := P.RAW_INTR_STAT.TX_EMPTY; + exit when TX_EMPTY; + Timeout := Time_Exceeded (This); + TX_Abort := Timeout; + exit when Timeout; + end loop; + + if not Timeout then + TX_ABRT_SOURCE := P.TX_ABRT_SOURCE; + if TX_ABRT_SOURCE > 0 then + Read_Clear := P.CLR_TX_ABRT; + TX_Abort := True; + end if; + + if TX_Abort or else ((I = Data'Last) and then Stop) then + loop + Timeout := Time_Exceeded (This); + TX_Abort := TX_Abort or else Timeout; + STOP_DET := P.RAW_INTR_STAT.STOP_DET; + exit when Timeout or else STOP_DET; + end loop; + + if not Timeout then + Read_Clear := P.CLR_STOP_DET; + end if; + end if; end if; + + exit when TX_Abort; end loop; - case This.Address_Size is - when Address_Size_7b => - This.Port.Set_Address (HAL.UInt7 (Bus_Addr)); - when Address_Size_10b => - This.Port.Set_Address (HAL.UInt10 (Bus_Addr)); - end case; + This.Restart_On_Next := not Stop; + Error := TX_Abort; + end Write; - This.Port.Enable (Deadline); - while not This.Port.Enabled loop - if RP.Timer.Clock >= Deadline then - Status := HAL.I2C.Err_Timeout; - return; - end if; + procedure Read + (This : in out I2C_Master_Port; + Addr : UInt10; + Data : out UInt8_Array; + Error : out Boolean; + Stop : Boolean := True) + is + P : constant Any_I2C_Peripheral := Periph (This); + TX_BUFFER_DEPTH : constant := 16; + Timeout : Boolean; + RX_Abort : Boolean := False; + TXFLR, RXFLR : UInt32; + TX_ABRT_SOURCE : UInt32; + Read_Clear : CLR_Register with Volatile; + begin + P.ENABLE.ENABLE := False; + P.TAR := (TAR => Addr, others => <>); + P.ENABLE.ENABLE := True; + + Data := (others => 0); + + for I in Data'Range loop + loop + TXFLR := P.TXFLR; + exit when (TX_BUFFER_DEPTH - TXFLR) > 0; + end loop; + + P.DATA_CMD := + (RESTART => (I = Data'First) and then This.Restart_On_Next, + STOP => (I = Data'Last) and then Stop, + CMD => True, + others => <>); + + loop + TX_ABRT_SOURCE := P.TX_ABRT_SOURCE; + if TX_ABRT_SOURCE > 0 then + Read_Clear := P.CLR_TX_ABRT; + RX_Abort := True; + end if; + Timeout := Time_Exceeded (This); + RX_Abort := RX_Abort or else Timeout; + RXFLR := P.RXFLR; + exit when RX_Abort or else RXFLR > 0; + end loop; + + exit when RX_Abort; + + Data (I) := P.DATA_CMD.DAT; end loop; - Status := HAL.I2C.Ok; - end Set_Address; + This.Restart_On_Next := not Stop; + Error := RX_Abort; + end Read; overriding procedure Master_Transmit @@ -88,34 +358,23 @@ package body RP.I2C_Master is Status : out HAL.I2C.I2C_Status; Timeout : Natural := 1000) is - use RP.Timer; - Deadline : constant Time := RP.Timer.Clock + Milliseconds (Timeout); - S : RP.I2C.I2C_Status; + use type RP.Timer.Time; + Error : Boolean; begin - This.Set_Address (Addr, Status, Deadline); - if Status /= HAL.I2C.Ok then - return; + This.Deadline := RP.Timer.Clock + RP.Timer.Milliseconds (Timeout); + Write + (This => This, + Addr => UInt10 (Addr), + Data => UInt8_Array (Data), + Error => Error, + Stop => True); + if Time_Exceeded (This) then + Status := HAL.I2C.Err_Timeout; + elsif Error then + Status := HAL.I2C.Err_Error; + else + Status := HAL.I2C.Ok; end if; - - This.Port.Start_Write (Data'Length, Deadline => Deadline); - for D of Data loop - This.Port.Write (D, S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Abort_Write; - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - end loop; - - while not This.Port.State.TX_Empty loop - if Clock >= Deadline then - Status := HAL.I2C.Err_Timeout; - return; - end if; - end loop; - - Status := HAL.I2C.Ok; end Master_Transmit; overriding @@ -126,27 +385,41 @@ package body RP.I2C_Master is Status : out HAL.I2C.I2C_Status; Timeout : Natural := 1000) is - use RP.Timer; - Deadline : constant Time := RP.Timer.Clock + Milliseconds (Timeout); - S : RP.I2C.I2C_Status; + use type RP.Timer.Time; + Error : Boolean; begin - This.Set_Address (Addr, Status, Deadline); - if Status /= HAL.I2C.Ok then - return; + This.Deadline := RP.Timer.Clock + RP.Timer.Milliseconds (Timeout); + Read + (This => This, + Addr => UInt10 (Addr), + Data => UInt8_Array (Data), + Error => Error, + Stop => True); + if Time_Exceeded (This) then + Status := HAL.I2C.Err_Timeout; + elsif Error then + Status := HAL.I2C.Err_Error; + else + Status := HAL.I2C.Ok; end if; - - This.Port.Start_Read (Data'Length); - for I in Data'Range loop - This.Port.Read (Data (I), S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - end loop; - Status := HAL.I2C.Ok; end Master_Receive; + function Mem_Addr_Data + (Size : HAL.I2C.I2C_Memory_Address_Size; + Addr : HAL.UInt16) + return HAL.UInt8_Array + is + use HAL.I2C; + begin + case Size is + when Memory_Size_8b => + return UInt8_Array'(1 => UInt8 (Addr)); + when Memory_Size_16b => + return UInt8_Array'(1 => UInt8 (Shift_Right (Addr, 8)), + 2 => UInt8 (Addr and 16#FF#)); + end case; + end Mem_Addr_Data; + overriding procedure Mem_Write (This : in out I2C_Master_Port; @@ -157,61 +430,35 @@ package body RP.I2C_Master is Status : out HAL.I2C.I2C_Status; Timeout : Natural := 1000) is - use RP.Timer; - Deadline : constant Time := RP.Timer.Clock + Milliseconds (Timeout); - S : RP.I2C.I2C_Status; + use type RP.Timer.Time; + Error : Boolean; begin - This.Set_Address (Addr, Status, Deadline); - if Status /= HAL.I2C.Ok then - return; + This.Deadline := RP.Timer.Clock + RP.Timer.Milliseconds (Timeout); + Write + (This => This, + Addr => UInt10 (Addr), + Data => Mem_Addr_Data (Mem_Addr_Size, Mem_Addr), + Error => Error, + Stop => False); + if not Error then + Write + (This => This, + Addr => UInt10 (Addr), + Data => UInt8_Array (Data), + Error => Error, + Stop => True); end if; - case Mem_Addr_Size is - when HAL.I2C.Memory_Size_8b => - This.Port.Start_Write (Data'Length + 1); - This.Port.Write (UInt8 (Mem_Addr), S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Abort_Write; - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - when HAL.I2C.Memory_Size_16b => - This.Port.Start_Write (Data'Length + 2); - This.Port.Write (UInt8 (Shift_Right (Mem_Addr, 8)), S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Abort_Write; - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - This.Port.Write (UInt8 (Mem_Addr and 16#FF#), S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Abort_Write; - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - end case; - - for D of Data loop - This.Port.Write (D, S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Abort_Write; - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - end loop; - - while not This.Port.State.TX_Empty loop - if Clock >= Deadline then - Status := HAL.I2C.Err_Timeout; - return; - end if; - end loop; - - Status := HAL.I2C.Ok; + if Time_Exceeded (This) then + Status := HAL.I2C.Err_Timeout; + return; + elsif Error then + Status := HAL.I2C.Err_Error; + return; + else + Status := HAL.I2C.Ok; + return; + end if; end Mem_Write; overriding @@ -224,55 +471,35 @@ package body RP.I2C_Master is Status : out HAL.I2C.I2C_Status; Timeout : Natural := 1000) is - use RP.Timer; - Deadline : constant Time := RP.Timer.Clock + Milliseconds (Timeout); - S : RP.I2C.I2C_Status; + use type RP.Timer.Time; + Error : Boolean; begin - This.Set_Address (Addr, Status, Deadline); - if Status /= HAL.I2C.Ok then - return; + This.Deadline := RP.Timer.Clock + RP.Timer.Milliseconds (Timeout); + Write + (This => This, + Addr => UInt10 (Addr), + Data => Mem_Addr_Data (Mem_Addr_Size, Mem_Addr), + Error => Error, + Stop => False); + if not Error then + Read + (This => This, + Addr => UInt10 (Addr), + Data => UInt8_Array (Data), + Error => Error, + Stop => True); end if; - case Mem_Addr_Size is - when HAL.I2C.Memory_Size_8b => - This.Port.Start_Write (1, Stop => False); - This.Port.Write (UInt8 (Mem_Addr), S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Abort_Write; - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - when HAL.I2C.Memory_Size_16b => - This.Port.Start_Write (2, Stop => False); - This.Port.Write (UInt8 (Shift_Right (Mem_Addr, 8)), S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Abort_Write; - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - This.Port.Write (UInt8 (Mem_Addr and 16#FF#), S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Abort_Write; - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - end case; - - This.Port.Start_Read (Data'Length); - - for I in Data'Range loop - This.Port.Read (Data (I), S, Deadline); - if S /= RP.I2C.Ok then - This.Port.Clear_Error; - Status := To_HAL_Status (S); - return; - end if; - end loop; - - Status := HAL.I2C.Ok; + if Time_Exceeded (This) then + Status := HAL.I2C.Err_Timeout; + return; + elsif Error then + Status := HAL.I2C.Err_Error; + return; + else + Status := HAL.I2C.Ok; + return; + end if; end Mem_Read; end RP.I2C_Master; diff --git a/src/drivers/rp-i2c_master.ads b/src/drivers/rp-i2c_master.ads index 9bf3e1f..2c89e3e 100644 --- a/src/drivers/rp-i2c_master.ads +++ b/src/drivers/rp-i2c_master.ads @@ -1,26 +1,19 @@ -- --- Copyright 2021 (C) Jeremy Grosser +-- Copyright 2021-2024 (C) Jeremy Grosser -- -- SPDX-License-Identifier: BSD-3-Clause -- -private with RP.I2C; -private with RP.Timer; +with RP.Timer; +with HAL; use HAL; with HAL.I2C; -with RP2040_SVD.I2C; -with HAL; --- This package implements the HAL.I2C interface by using RP.I2C. --- Applications that do not require the portability of the HAL.I2C interface --- are encouraged to use RP.I2C directly, as it provides more control and --- flexibility. package RP.I2C_Master with Preelaborate is subtype I2C_Number is Natural range 0 .. 1; type I2C_Master_Port - (Num : I2C_Number; - Periph : not null access RP2040_SVD.I2C.I2C_Peripheral) + (Num : I2C_Number) is new HAL.I2C.I2C_Port with private; type I2C_Address_Size is (Address_Size_7b, Address_Size_10b); @@ -51,7 +44,7 @@ is procedure Mem_Write (This : in out I2C_Master_Port; Addr : HAL.I2C.I2C_Address; - Mem_Addr : HAL.UInt16; + Mem_Addr : UInt16; Mem_Addr_Size : HAL.I2C.I2C_Memory_Address_Size; Data : HAL.I2C.I2C_Data; Status : out HAL.I2C.I2C_Status; @@ -61,30 +54,37 @@ is procedure Mem_Read (This : in out I2C_Master_Port; Addr : HAL.I2C.I2C_Address; - Mem_Addr : HAL.UInt16; + Mem_Addr : UInt16; Mem_Addr_Size : HAL.I2C.I2C_Memory_Address_Size; Data : out HAL.I2C.I2C_Data; Status : out HAL.I2C.I2C_Status; Timeout : Natural := 1000); + procedure Set_Deadline + (This : in out I2C_Master_Port; + Deadline : RP.Timer.Time); + + procedure Write + (This : in out I2C_Master_Port; + Addr : UInt10; + Data : UInt8_Array; + Error : out Boolean; + Stop : Boolean := True); + + procedure Read + (This : in out I2C_Master_Port; + Addr : UInt10; + Data : out UInt8_Array; + Error : out Boolean; + Stop : Boolean := True); private type I2C_Master_Port - (Num : I2C_Number; - Periph : not null access RP2040_SVD.I2C.I2C_Peripheral) + (Num : I2C_Number) is new HAL.I2C.I2C_Port with record - Port : RP.I2C.I2C_Port (Num, Periph); - Address_Size : I2C_Address_Size; + Address_Size : I2C_Address_Size; + Restart_On_Next : Boolean := False; + Deadline : RP.Timer.Time; end record; - procedure Set_Address - (This : in out I2C_Master_Port; - Addr : HAL.I2C.I2C_Address; - Status : out HAL.I2C.I2C_Status; - Deadline : RP.Timer.Time); - - function To_HAL_Status - (S : RP.I2C.I2C_Status) - return HAL.I2C.I2C_Status; - end RP.I2C_Master;