1 //
2 // Copyright (c) 2010-2021 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 using System;
9 using System.IO;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Sensor;
15 using Antmicro.Renode.Peripherals.I2C;
16 using Antmicro.Renode.Peripherals.SPI;
17 using Antmicro.Renode.Utilities;
18 using Antmicro.Renode.HostInterfaces.Camera;
19 
20 namespace Antmicro.Renode.Peripherals.Sensors
21 {
22     public class ArduCAMMini2MPPlus: ISPIPeripheral, II2CPeripheral, IGPIOReceiver, IProvidesRegisterCollection<ByteRegisterCollection>, ISensor
23     {
ArduCAMMini2MPPlus()24         public ArduCAMMini2MPPlus()
25         {
26             sensor = new OV2640(this);
27 
28             RegistersCollection = new ByteRegisterCollection(this);
29             DefineRegisters();
30         }
31 
32         #region SPI_Interface
33 
Transmit(byte data)34         public byte Transmit(byte data)
35         {
36             byte result = 0;
37 
38             switch(state)
39             {
40                 case State.Idle:
41                 {
42                     selectedRegister = (Register)BitHelper.GetValue(data, 0, 7);
43                     var isWrite = BitHelper.IsBitSet(data, 7);
44 
45                     this.NoisyLog("Decoded register {0} (0x{0:X}) and isWrite bit as {1}", selectedRegister, isWrite);
46                     state = isWrite
47                         ? State.Writing
48                         : State.Reading;
49 
50                     break;
51                 }
52 
53                 case State.Reading:
54                     this.NoisyLog("Reading register {0} (0x{0:X})", selectedRegister);
55                     result = RegistersCollection.Read((long)selectedRegister);
56                     break;
57 
58                 case State.Writing:
59                     this.NoisyLog("Writing 0x{0:X} to register {1} (0x{1:X})", data, selectedRegister);
60                     RegistersCollection.Write((long)selectedRegister, data);
61                     break;
62 
63                 default:
64                     this.Log(LogLevel.Error, "Received byte in an unexpected state: {0}", state);
65                     break;
66             }
67 
68             this.NoisyLog("Received byte 0x{0:X}, returning 0x{1:X}", data, result);
69             return result;
70         }
71 
ISPIPeripheral.FinishTransmission()72         void ISPIPeripheral.FinishTransmission()
73         {
74             this.NoisyLog("Finishing transmission, going idle");
75             state = State.Idle;
76         }
77 
OnGPIO(int number, bool value)78         public void OnGPIO(int number, bool value)
79         {
80             if(number != 0)
81             {
82                 this.Log(LogLevel.Warning, "This model supports only CS on pin 0, but got signal on pin {0}", number);
83                 return;
84             }
85 
86             // value is the negated CS
87             if(chipSelected && value)
88             {
89                 ((ISPIPeripheral)this).FinishTransmission();
90             }
91             chipSelected = !value;
92         }
93 
94         #endregion
95 
96         #region I2C_Interface
97 
Write(byte[] data)98         public void Write(byte[] data)
99         {
100             sensor.Write(data);
101         }
102 
Read(int count)103         public byte[] Read(int count)
104         {
105             return sensor.Read(count);
106         }
107 
II2CPeripheral.FinishTransmission()108         void II2CPeripheral.FinishTransmission()
109         {
110             sensor.FinishTransmission();
111         }
112 
113         #endregion
114 
Reset()115         public void Reset()
116         {
117             RegistersCollection.Reset();
118 
119             sensor.Reset();
120             chipSelected = false;
121             selectedRegister = Register.Test;
122             state = State.Idle;
123         }
124 
125         public ByteRegisterCollection RegistersCollection { get; }
126 
127         public string ImageSource
128         {
129             get
130             {
131                 return imageSource;
132             }
133 
134             set
135             {
136                 if(value == null)
137                 {
138                     preloadedImageData = null;
139                     imageSource = null;
140                     return;
141                 }
142 
143                 try
144                 {
145                     preloadedImageData = File.ReadAllBytes(value);
146                     imageSource = value;
147 
148                     this.NoisyLog("Loaded {0} bytes of image data to buffer", preloadedImageData.Length);
149                 }
150                 catch(Exception e)
151                 {
152                     throw new RecoverableException($"Could not load image {value}: {(e.Message)}");
153                 }
154             }
155         }
156 
AttachToExternalCamera(HostCamera camera)157         public void AttachToExternalCamera(HostCamera camera)
158         {
159 #if PLATFORM_LINUX
160             externalCamera = camera;
161 #else
162             throw new RecoverableException("The external camera integration is currently available on Linux only!");
163 #endif
164         }
165 
DetachFromExternalCamera()166         public void DetachFromExternalCamera()
167         {
168 #if PLATFORM_LINUX
169             externalCamera = null;
170 #else
171             throw new RecoverableException("The external camera integration is currently available on Linux only!");
172 #endif
173         }
174 
DefineRegisters()175         private void DefineRegisters()
176         {
177             Register.Test.Define(this)
178                 .WithValueField(0, 8, name: "Test field")
179             ;
180 
181             Register.FifoControl.Define(this)
182                 .WithFlag(0, FieldMode.Write, name: "Clear FIFO write done flag", writeCallback: (_, val) =>
183                 {
184                     if(val)
185                     {
186                         fifoDone.Value = false;
187                     }
188                 })
189                 .WithFlag(1, FieldMode.Write, name: "Start capture", writeCallback: (_, val) =>
190                 {
191                     if(!val)
192                     {
193                         return;
194                     }
195 
196                     this.NoisyLog("Capturing frame");
197 #if PLATFORM_LINUX
198                     var ec = externalCamera;
199                     if(ec != null)
200                     {
201                         ec.SetImageSize((int)sensor.OutputWidth, (int)sensor.OutputHeight);
202                         imageData = ec.GrabFrame();
203                     }
204                     else if(preloadedImageData != null)
205 #else
206                     if(preloadedImageData != null)
207 #endif
208                     {
209                         imageData = preloadedImageData;
210                     }
211                     else
212                     {
213                         this.Log(LogLevel.Warning, "No image source set. Generating an empty frame");
214                         imageData = new byte[0];
215                     }
216 
217                     imageDataPointer = 0;
218                     fifoDone.Value = true;
219                 })
220                 .WithReservedBits(2, 2)
221                 .WithTaggedFlag("Reset FIFO read pointer", 4)
222                 .WithTaggedFlag("Reset FIFO write pointer", 5)
223                 .WithReservedBits(6, 1)
224             ;
225 
226             Register.BurstRead.Define(this)
227                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ =>
228                 {
229                     if(imageDataPointer >= imageData.Length)
230                     {
231                         this.Log(LogLevel.Warning, "Tried to read outside the image buffer");
232                         return 0;
233                     }
234 
235                     return imageData[imageDataPointer++];
236                 })
237             ;
238 
239             Register.TriggerSource.Define(this)
240                 .WithTaggedFlag("Camera vsync pin status", 0)
241                 .WithReservedBits(1, 2)
242                 .WithFlag(3, out fifoDone, name: "Camera write FIFO done")
243                 .WithReservedBits(4, 3)
244             ;
245 
246             Register.CameraWriteFifoSize0.Define(this)
247                 .WithValueField(0, 8, valueProviderCallback: _ => (byte)(imageData?.Length ?? 0))
248             ;
249 
250             Register.CameraWriteFifoSize1.Define(this)
251                 .WithValueField(0, 8, valueProviderCallback: _ => (byte)((imageData?.Length ?? 0) >> 8))
252             ;
253 
254             Register.CameraWriteFifoSize2.Define(this)
255                 .WithValueField(0, 8, valueProviderCallback: _ => (byte)((imageData?.Length ?? 0) >> 16))
256             ;
257         }
258 
259         private bool chipSelected;
260         private Register selectedRegister;
261         private State state;
262 
263         private int imageDataPointer;
264         private byte[] imageData;
265         private byte[] preloadedImageData;
266         private string imageSource;
267 
268         private IFlagRegisterField fifoDone;
269 
270         private readonly OV2640 sensor;
271 
272 #if PLATFORM_LINUX
273         private HostCamera externalCamera;
274 #endif
275 
276         private enum State
277         {
278             Idle,
279             Reading,
280             Writing
281         }
282 
283         private enum Register
284         {
285             Test = 0x00,
286             CaptureControl = 0x01,
287             Reserved02 = 0x02,
288             SensorInterfaceTiming = 0x03,
289             FifoControl = 0x04,
290             GPIODirection = 0x05,
291             GPIOWrite = 0x06,
292             // this gap is not covered by the documentation
293             Reserved3B = 0x3B,
294             BurstRead = 0x3C,
295             SingleRead = 0x3D,
296             Reserved3E = 0x3E,
297             Reserved3F = 0x3F,
298             ArduChipVersion = 0x40,
299             TriggerSource = 0x41,
300             CameraWriteFifoSize0 = 0x42,
301             CameraWriteFifoSize1 = 0x43,
302             CameraWriteFifoSize2 = 0x44,
303             GPIOReadRegister = 0x45
304         }
305     }
306 }
307