From e2e3397859dbe64eebde1ea452f10682bf6ffe19 Mon Sep 17 00:00:00 2001 From: Jeremy Grosser Date: Sat, 16 Nov 2024 12:08:48 -0800 Subject: [PATCH] More RP2350 setup: pll, ticks, IO isolation, I2C --- .../rp2040}/rp-i2c_master.adb | 0 src/devices/rp2350/rp-clock.adb | 160 ++++-- src/devices/rp2350/rp-gpio.adb | 3 + src/devices/rp2350/rp-i2c_master.adb | 506 ++++++++++++++++++ 4 files changed, 617 insertions(+), 52 deletions(-) rename src/{drivers => devices/rp2040}/rp-i2c_master.adb (100%) create mode 100644 src/devices/rp2350/rp-i2c_master.adb diff --git a/src/drivers/rp-i2c_master.adb b/src/devices/rp2040/rp-i2c_master.adb similarity index 100% rename from src/drivers/rp-i2c_master.adb rename to src/devices/rp2040/rp-i2c_master.adb diff --git a/src/devices/rp2350/rp-clock.adb b/src/devices/rp2350/rp-clock.adb index 615d27d..40ed582 100644 --- a/src/devices/rp2350/rp-clock.adb +++ b/src/devices/rp2350/rp-clock.adb @@ -4,7 +4,8 @@ -- SPDX-License-Identifier: BSD-3-Clause -- with RP2350_SVD.CLOCKS; use RP2350_SVD.CLOCKS; -with RP2040_SVD.PLL; use RP2040_SVD.PLL; +with RP2350_SVD.PLL; use RP2350_SVD.PLL; +with RP2350_SVD.TICKS; with RP2350_SVD.XOSC; with RP2350_SVD.ROSC; with RP.Reset; @@ -13,20 +14,24 @@ package body RP.Clock is procedure Enable_XOSC is use RP2350_SVD.XOSC; + STATUS : STATUS_Register; begin XOSC_Periph.CTRL.FREQ_RANGE := Val_1_15MHZ; XOSC_Periph.CTRL.ENABLE := ENABLE; - while not XOSC_Periph.STATUS.STABLE loop - null; + loop + STATUS := XOSC_Periph.STATUS; + exit when STATUS.STABLE; end loop; end Enable_XOSC; procedure Enable_ROSC is use RP2350_SVD.ROSC; + STATUS : STATUS_Register; begin ROSC_Periph.CTRL.ENABLE := ENABLE; - while not ROSC_Periph.STATUS.STABLE loop - null; + loop + STATUS := ROSC_Periph.STATUS; + exit when STATUS.STABLE; end loop; end Enable_ROSC; @@ -38,29 +43,58 @@ package body RP.Clock is (if PLL = PLL_SYS then PLL_SYS_Periph'Access else PLL_USB_Periph'Access); begin -- Ensure PLL is stopped before configuring - Periph.PWR := (others => <>); + Periph.PWR := + (PD => True, + DSMPD => True, + POSTDIVPD => True, + VCOPD => True, + Reserved_1_1 => 0, + Reserved_4_4 => 0, + Reserved_6_31 => 0); - Periph.CS.REFDIV := Config.REFDIV; - Periph.FBDIV_INT.FBDIV_INT := Config.FBDIV; + Periph.CS := + (REFDIV => Config.REFDIV, + BYPASS => False, + LOCK_N => False, + LOCK => False, + Reserved_6_7 => 0, + Reserved_9_29 => 0); + + Periph.FBDIV_INT := + (FBDIV_INT => Config.FBDIV, + Reserved_12_31 => 0); -- Turn on PLL - Periph.PWR.PD := False; - Periph.PWR.VCOPD := False; + Periph.PWR := + (PD => False, + DSMPD => True, + POSTDIVPD => True, + VCOPD => False, + Reserved_1_1 => 0, + Reserved_4_4 => 0, + Reserved_6_31 => 0); -- Wait for lock - while not Periph.CS.LOCK loop - null; - end loop; + declare + CS : CS_Register; + begin + loop + CS := Periph.CS; + exit when CS.LOCK; + end loop; + end; -- Setup post dividers - Periph.PRIM.POSTDIV1 := Config.POSTDIV1; - Periph.PRIM.POSTDIV2 := Config.POSTDIV2; + Periph.PRIM := + (POSTDIV1 => Config.POSTDIV1, + POSTDIV2 => Config.POSTDIV2, + Reserved_0_11 => 0, + Reserved_15_15 => 0, + Reserved_19_31 => 0); Periph.PWR.POSTDIVPD := False; end Configure_PLL; - procedure Init_PLLs - (XOSC_Frequency : Hertz) - is + procedure Init_PLLs is use RP.Reset; begin -- Reset PLLs @@ -71,30 +105,16 @@ package body RP.Clock is Configure_PLL (PLL_SYS, PLL_125_MHz); Configure_PLL (PLL_USB, PLL_48_MHz); - -- Switch clk_sys to pll_sys + -- Switch clk_sys to pll_sys (glitchless) CLOCKS_Periph.CLK_SYS_CTRL.AUXSRC := clksrc_pll_sys; CLOCKS_Periph.CLK_SYS_DIV := (INT => 1, FRAC => 0); CLOCKS_Periph.CLK_SYS_CTRL.SRC := clksrc_clk_sys_aux; - -- Switch clk_usb to pll_usb - -- CLOCKS_Periph.CLK (USB).DIV.INT := 1; - -- CLOCKS_Periph.CLK (USB).CTRL.AUXSRC := PLL_SYS; -- the AUXSRC enum has the wrong name for some registers - -- while CLOCKS_Periph.CLK (USB).SELECTED /= CLK_SELECTED_Mask (USB_SRC_USB) loop - -- null; - -- end loop; - - -- Switch clk_adc to pll_usb - -- CLOCKS_Periph.CLK (ADC).DIV.INT := 1; - -- CLOCKS_Periph.CLK (ADC).CTRL.AUXSRC := PLL_SYS; - -- while CLOCKS_Periph.CLK (ADC).SELECTED /= CLK_SELECTED_Mask (ADC_SRC_USB) loop - -- null; - -- end loop; + -- clk_usb and clk_adc use pll_usb by default after reset, no need to + -- configure them. - -- Switch clk_peri to pll_sys - -- CLOCKS_Periph.CLK (PERI).CTRL.AUXSRC := PLL_SYS; - -- while CLOCKS_Periph.CLK (PERI).SELECTED /= CLK_SELECTED_Mask (PERI_SRC_SYS) loop - -- null; - -- end loop; + -- Switch clk_peri to pll_sys (might glitch) + CLOCKS_Periph.CLK_PERI_CTRL.AUXSRC := clksrc_pll_sys; end Init_PLLs; procedure Initialize @@ -110,34 +130,34 @@ package body RP.Clock is -- Enable RESUS in case things go badly CLOCKS_Periph.CLK_SYS_RESUS_CTRL.ENABLE := True; - -- Enable watchdog and maybe XOSC if XOSC_Frequency > 0 then - Reference := XOSC_Frequency; + -- Enable XOSC, set it as clk_ref source RP2350_SVD.XOSC.XOSC_Periph.STARTUP.DELAY_k := RP2350_SVD.XOSC.STARTUP_DELAY_Field (XOSC_Startup_Delay / 256); - CLOCKS_Periph.CLK_SYS_CTRL.AUXSRC := xosc_clksrc; - CLOCKS_Periph.CLK_SYS_DIV := (INT => 1, FRAC => 0); - CLOCKS_Periph.CLK_SYS_CTRL.SRC := clksrc_clk_sys_aux; - -- Init_PLLs (XOSC_Frequency); + Enable_XOSC; + CLOCKS_Periph.CLK_REF_CTRL.SRC := xosc_clksrc; + Reference := XOSC_Frequency; + + -- Run clk_sys directly from clk_ref while configuring PLLs + CLOCKS_Periph.CLK_SYS_CTRL.SRC := clk_ref; + + -- Configure PLLs and select them as the source for all clocks + Init_PLLs; else - Reference := ROSC_Frequency; - CLOCKS_Periph.CLK_SYS_CTRL.AUXSRC := rosc_clksrc; - CLOCKS_Periph.CLK_SYS_DIV := (INT => 1, FRAC => 0); - CLOCKS_Periph.CLK_SYS_CTRL.SRC := clksrc_clk_sys_aux; - Disable (PLL_SYS); - Disable (PLL_USB); - Disable (XOSC); + Reference := 11_000_000; -- nominal ROSC frequency, much variance end if; CLOCKS_Periph.FC0_REF_KHZ.FC0_REF_KHZ := FC0_REF_KHZ_FC0_REF_KHZ_Field (Reference / 1_000); + RP2350_SVD.TICKS.TICKS_Periph.TIMER0_CTRL.ENABLE := True; end Initialize; procedure Enable (CID : Clock_Id) is + P : CLOCKS_Peripheral renames CLOCKS_Periph; begin case CID is when GPOUT0 => - CLOCKS_Periph.CLK_GPOUT0_CTRL.ENABLE := True; + P.CLK_GPOUT0_CTRL.ENABLE := True; when GPOUT1 => CLOCKS_Periph.CLK_GPOUT1_CTRL.ENABLE := True; when GPOUT2 => @@ -265,7 +285,43 @@ package body RP.Clock is Source : GP_Source) is begin - raise Clock_Error with "Not implemented"; + case GP is + when GPOUT0 => + case Source is + when PLL_SYS => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clksrc_pll_sys; + when GPIN0 => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clksrc_gpin0; + when GPIN1 => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clksrc_gpin1; + when PLL_USB => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clksrc_pll_usb; + when ROSC => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := rosc_clksrc; + when XOSC => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := xosc_clksrc; + when SYS => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clk_sys; + when USB => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clk_usb; + when ADC => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clk_adc; + when RTC => + raise Clock_Error with "RTC not supported on RP2350"; + when REF => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clk_ref; + when PERI => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clk_peri; + when HSTX => + CLOCKS_Periph.CLK_GPOUT0_CTRL.AUXSRC := clk_hstx; + -- Not mapped in RP.Clock: + -- lposc_clksrc + -- clksrc_pll_usb_primary_ref_opcg + -- otp_clk2fc + end case; + when others => + raise Clock_Error with "Not implemented"; + end case; end Set_Source; procedure Set_Divider diff --git a/src/devices/rp2350/rp-gpio.adb b/src/devices/rp2350/rp-gpio.adb index 8c94938..757ad1e 100644 --- a/src/devices/rp2350/rp-gpio.adb +++ b/src/devices/rp2350/rp-gpio.adb @@ -71,6 +71,7 @@ package body RP.GPIO is SCHMITT => Schmitt, SLEWFAST => Slew_Fast, DRIVE => GPIO0_DRIVE_Field'Val (GPIO_Drive'Pos (Drive)), + ISO => False, others => <>); when Output => PADS_BANK_Periph.GPIO (This.Pin) := @@ -81,6 +82,7 @@ package body RP.GPIO is SCHMITT => Schmitt, SLEWFAST => Slew_Fast, DRIVE => GPIO0_DRIVE_Field'Val (GPIO_Drive'Pos (Drive)), + ISO => False, others => <>); SIO_Periph.GPIO_OUT_CLR := Mask; SIO_Periph.GPIO_OE_SET := Mask; @@ -93,6 +95,7 @@ package body RP.GPIO is SCHMITT => Schmitt, SLEWFAST => Slew_Fast, DRIVE => GPIO0_DRIVE_Field'Val (GPIO_Drive'Pos (Drive)), + ISO => False, others => <>); IO_BANK_Periph.GPIO (This.Pin).CTRL.FUNCSEL := HI_Z; end case; diff --git a/src/devices/rp2350/rp-i2c_master.adb b/src/devices/rp2350/rp-i2c_master.adb new file mode 100644 index 0000000..4d5bebf --- /dev/null +++ b/src/devices/rp2350/rp-i2c_master.adb @@ -0,0 +1,506 @@ +-- +-- Copyright 2022-2024 (C) Jeremy Grosser +-- +-- SPDX-License-Identifier: BSD-3-Clause +-- +with RP.Clock; +with System; + +package body RP.I2C_Master is + 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; + + 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#4009_0000#); + I2C_1 : aliased I2C_Peripheral + with Import, Address => System'To_Address (16#4009_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 + 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; + This.Deadline := RP.Timer.Time'First; + end Configure; + + procedure Set_Deadline + (This : in out I2C_Master_Port; + Deadline : RP.Timer.Time) + is + begin + 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); + + STAT : INTR_Register; + 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 + STAT := P.RAW_INTR_STAT; + exit when STAT.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; + STAT := P.RAW_INTR_STAT; + exit when Timeout or else STAT.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; + + This.Restart_On_Next := not Stop; + Error := TX_Abort; + end Write; + + 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; + + This.Restart_On_Next := not Stop; + Error := RX_Abort; + end Read; + + overriding + procedure Master_Transmit + (This : in out I2C_Master_Port; + Addr : HAL.I2C.I2C_Address; + Data : HAL.I2C.I2C_Data; + Status : out HAL.I2C.I2C_Status; + Timeout : Natural := 1000) + is + use type RP.Timer.Time; + Error : Boolean; + begin + 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; + end Master_Transmit; + + overriding + procedure Master_Receive + (This : in out I2C_Master_Port; + Addr : HAL.I2C.I2C_Address; + Data : out HAL.I2C.I2C_Data; + Status : out HAL.I2C.I2C_Status; + Timeout : Natural := 1000) + is + use type RP.Timer.Time; + Error : Boolean; + begin + 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; + 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; + Addr : HAL.I2C.I2C_Address; + Mem_Addr : HAL.UInt16; + Mem_Addr_Size : HAL.I2C.I2C_Memory_Address_Size; + Data : HAL.I2C.I2C_Data; + Status : out HAL.I2C.I2C_Status; + Timeout : Natural := 1000) + is + use type RP.Timer.Time; + Error : Boolean; + begin + 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; + + 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 + procedure Mem_Read + (This : in out I2C_Master_Port; + Addr : HAL.I2C.I2C_Address; + Mem_Addr : HAL.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) + is + use type RP.Timer.Time; + Error : Boolean; + begin + 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; + + 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;