BBS_BBB_Ada_17b6af3a/src-due/bbs-embed-i2c-due.ads

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
--
--  Author: Brent Seidel
--  Date: 9-Aug-2024
--
--  This file is part of bbs_embed.
--  Bbs_embed is free software: you can redistribute it and/or modify it
--  under the terms of the GNU General Public License as published by the
--  Free Software Foundation, either version 3 of the License, or (at your
--  option) any later version.
--
--  bbs_embed is distributed in the hope that it will be useful, but
--  WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
--  Public License for more details.
--
--  You should have received a copy of the GNU General Public License along
--  with bbs_embed. If not, see <https://www.gnu.org/licenses/>.--
--
with Ada.Interrupts;
with Ada.Interrupts.Names;
with Ada.Real_Time;
use type Ada.Real_Time.Time;
use type Ada.Real_Time.Time_Span;
with Ada.Synchronous_Task_Control;
with System;
with SAM3x8e.TWI;
with BBS.embed.GPIO.Due;
with BBS.embed.due.dev;
--
--  Concrete package for I2C interfaces on the Arduino Due.  This inherits from
--  the base class and adds the actual functionality to implement the interface.
--
--  Eventually, any dependecies on the SAM3x8e package tree should be eliminated.
--
--  The Arduino Due has two I2C interfaces.
--  Interface  SCL  SDA   TWI
--  I2C-0      PB13 PB12  TWI0
--  I2C-1      PA18 PA17  TWI1
--
package bbs.embed.i2c.due is
   --
   --  Interface speed, 100kHz and 400kHz are supported.
   --
   type speed_type is (low100, high400);
   --
   --  Port ID is 0 or 1
   --
   type port_id is  new Integer range 0 .. 1;
   --
   --  The I2C device object
   --
   type due_i2c_interface_record is new i2c_interface_record with private;
   type due_i2c_interface is access all due_i2c_interface_record'Class;
   --
   --  Function to return access to a device record.
   --
   function get_interface(d : port_id) return due_i2c_interface;
   --
   --  Initialize interface I2C-0 on the Arduino (turns out to be TWI1
   --  internally)
   --
   procedure init(chan : port_id; speed : speed_type);
   --
   --  Routines to read and write data on the i2c bus
   --
   procedure write(chan : port_id; addr : addr7; reg : uint8;
                   data : uint8; error : out err_code);
   function read(chan : port_id; addr : addr7; reg : uint8;
                 error : out err_code) return uint8;
   --
   --  Reading a single byte is straightforward.  When reading two bytes, is the
   --  MSB first or second?  There is no standard even within a single device.
   --
   --  Read a word with MSB first
   --
   function readm1(chan : port_id; addr : addr7; reg : uint8;
                   error : out err_code) return UInt16;
   --
   -- Read a word with MSB second (LSB first)
   --
   function readm2(chan : port_id; addr : addr7; reg : uint8;
                   error : out err_code) return UInt16;
   --
   -- Read the specified number of bytes into a buffer
   --
   procedure read(chan : port_id; addr : addr7; reg : uint8;
                  size : buff_index; error : out err_code);
   --
   --  Object oriented interface
   --
   overriding
   procedure write(self : in out due_i2c_interface_record; addr : addr7; reg : uint8;
                   data : uint8; error : out err_code);
   --
   overriding
   function read(self : in out due_i2c_interface_record; addr : addr7; reg : uint8;
                 error : out err_code) return uint8;
   --
   --  When reading or writing two bytes, is the MSB first or second?  There is
   --  no standard even within a single device.
   --
   --  Read a word with MSB first
   --
   overriding
   function readm1(self : in out due_i2c_interface_record; addr : addr7; reg : uint8;
                 error : out err_code) return UInt16;
   --
   --  Read a word with MSB second (LSB first)
   --
   overriding
   function readm2(self : in out due_i2c_interface_record; addr : addr7; reg : uint8;
                 error : out err_code) return UInt16;
   --
   --  Write an arbitrary number of bytes to a device on the i2c bus.
   --
   procedure write(self : in out due_i2c_interface_record; addr : addr7; reg : uint8;
                   size : buff_index; error : out err_code);
   --
   --  Read the specified number of bytes into a buffer
   --
   overriding
   procedure read(self : in out due_i2c_interface_record; addr : addr7; reg : uint8;
                  size : buff_index; error : out err_code);
   --
   --  Get the activity counter
   --
   function get_activity(self : in out due_i2c_interface_record) return uint32;
   --
   --  Get busy status
   --
   function is_busy(self : in out due_i2c_interface_record) return Boolean;
private
   --
   --  Delay time for I2C bus to clear between transactions.  On occasion, without
   --  a delay the bus will lock up with back to back transaction.  This may be
   --  more a problem with the I2C devices than with the processor since resetting
   --  the processor doesn't clear the problem.  Power also needs to be cycled
   --  on the I2C devices.
   --
   --  The delay time was emperically determined.  A value of 0.000_002 causes
   --  a lockup.  A value of 0.000_003 works.
   --
   i2c_delay : constant Ada.Real_Time.Time_Span := Ada.Real_Time.To_Time_Span(0.000_003);
   --
   --  Addresses for TWI records
   --
   TWI0 : aliased SAM3x8e.TWI.TWI_Peripheral
     with Import, Address => SAM3x8e.TWI0_Base;
   --
   TWI1 : aliased SAM3x8e.TWI.TWI_Peripheral
     with Import, Address => SAM3x8e.TWI1_Base;
   --
   --  A protected type defining the transmit and receive buffers as well as an
   --  interface to the buffers.  This is based on the serial port handler, but
   --  is a bit simpler since (a) tx and rx is not simultaneous, so only one
   --  buffer is needed, and (b) communications are more transaction/block
   --  oriented so the user only needs to be notified when the exchange is
   --  completed.
   --
   protected type handler(interrupt : Ada.Interrupts.Interrupt_ID) is
      --
      --  Set the address to the device record.  This only needs to be called
      --  once during initialization/configuration.
      --
      procedure set_interface(d : due_i2c_interface);
      --
      --  Functions to return statuses
      --
      function is_busy return Boolean;
      function get_status return SAM3x8e.TWI.TWI0_SR_Register;
      --
      --  Entry point to transmit a character.  Per Ravenscar, there can be
      --  only one entry.  This is not yet implemented.
      --
      entry send(addr : addr7; reg : uint8; size : buff_index);
      --
      --  Procedure to read a specified number of characters into a buffer.
      --  Calls to this procedure need to be synchronized using
      --  susp_not_busy.
      --
      procedure rx_read(addr : addr7; reg : uint8; size : buff_index);
      --
      -- Return various information.
      --
      function get_error return err_code;
      function get_complete return Boolean;
      function get_saved_status return SAM3x8e.TWI.TWI0_SR_Register;
   private
      procedure int_handler;
      pragma Attach_Handler (int_handler, interrupt);
      pragma Interrupt_Priority(System.Interrupt_Priority'First);

      device   : due_i2c_interface;

      not_busy : Boolean := True;
      complete : Boolean := False;
      status : SAM3x8e.TWI.TWI0_SR_Register;

      bytes  : buff_index;
      index  : buff_index;

      err : err_code;
   end handler;
   --
   --  Declare a handler for each i2c port
   --
   buff0 : aliased handler(Ada.Interrupts.Names.TWI1_Interrupt);
   buff1 : aliased handler(Ada.Interrupts.Names.TWI0_Interrupt);
   --
   --  An array of the interrupt handlers so that the I/O routines can access a
   --  handler by the port ID.
   --
   type buffer_access is access all handler;
   buff : array (port_id'Range) of buffer_access :=
     (buff0'access, buff1'access);
   --
   --  Create a serial channel information record.  This should contain all
   --  the information necessary to identify a serial port and the pins used
   --  by it.
   --
   type twi_access is access all SAM3x8e.TWI.TWI_Peripheral;
   type due_i2c_interface_record is new i2c_interface_record with
      record
         dev_id   : uint8;           --  TWI device ID
         port     : twi_access;      --  Access to I2C registers
         pioc     : BBS.embed.GPIO.Due.pio_access; --  PIO controlling pins
         sda_pin  : uint8;           --  SDA pin on PIO
         scl_pin  : uint8;           --  SCL pin on PIO
         int_id   : Ada.Interrupts.Interrupt_ID; -- Interrupt for channel
         handle   : buffer_access;
         not_busy : Ada.Synchronous_Task_Control.Suspension_Object;
         activity : uint32 := 0;
      end record;
   --
   --  The Arduino Due has two I2C busses available on the headers.  Note that
   --  the port numbers on the header are reversed from the internal hardware
   --  channel numbers.
   --
   i2c_0 : aliased due_i2c_interface_record;
   i2c_1 : aliased due_i2c_interface_record;
   i2c_port : array (port_id'Range) of due_i2c_interface := (i2c_0'Access, i2c_1'Access);

end bbs.embed.i2c.due;