1 // 2 // Copyright (c) 2010-2023 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.Logging; 13 using Antmicro.Renode.Peripherals.Sensor; 14 using Antmicro.Renode.Peripherals.I2C; 15 using Antmicro.Renode.Peripherals.SPI; 16 using Antmicro.Renode.Utilities; 17 18 namespace Antmicro.Renode.Peripherals.Sensors 19 { 20 public class OV2640 : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ISensor 21 { OV2640(IPeripheral parent)22 public OV2640(IPeripheral parent) 23 { 24 this.parent = parent; 25 26 dspRegisters = new ByteRegisterCollection(this); 27 sensorRegisters = new ByteRegisterCollection(this); 28 29 DefineDSPRegisters(); 30 DefineSensorRegisters(); 31 } 32 FinishTransmission()33 public void FinishTransmission() 34 { 35 parent.NoisyLog("OV2640: Finishing transmission, going idle"); 36 state = State.Idle; 37 } 38 Write(byte[] data)39 public void Write(byte[] data) 40 { 41 parent.NoisyLog("OV2640: Received the following bytes: {0}", Misc.PrettyPrintCollectionHex(data)); 42 foreach(var d in data) 43 { 44 Write(d); 45 } 46 } 47 Read(int count)48 public byte[] Read(int count) 49 { 50 parent.NoisyLog("OV2640: Reading from the device in state {0}", state); 51 switch(state) 52 { 53 case State.Processing: 54 { 55 var result = RegistersCollection.Read(address); 56 parent.NoisyLog("OV2640: Read 0x{0:X} from register {1}", result, DecodeRegister(address)); 57 return new [] { result }; 58 } 59 60 default: 61 parent.Log(LogLevel.Error, "OV2640: Reading in an unexpected state: {0}", state); 62 return new byte[0]; 63 } 64 } 65 Reset()66 public void Reset() 67 { 68 state = State.Idle; 69 sensorRegisterBankSelected = false; 70 address = 0; 71 72 dspRegisters.Reset(); 73 sensorRegisters.Reset(); 74 } 75 76 public OutputFormat Format => outputFormat.Value; 77 78 public ByteRegisterCollection RegistersCollection => sensorRegisterBankSelected ? sensorRegisters : dspRegisters; 79 80 public uint OutputWidth 81 { 82 get 83 { 84 return (uint)((zoomOutputWidthHigh.Value << 8) + zoomOutputWidthLow.Value) * 4; 85 } 86 } 87 88 public uint OutputHeight 89 { 90 get 91 { 92 return (uint)((zoomOutputHeightHigh.Value << 8) + zoomOutputHeightLow.Value) * 4; 93 } 94 } 95 Write(byte b)96 private void Write(byte b) 97 { 98 parent.NoisyLog("OV2640: Received byte 0x{0:X} in state {1}", b, state); 99 switch(state) 100 { 101 case State.Idle: 102 { 103 address = b; 104 state = State.Processing; 105 106 parent.NoisyLog("OV2640: Selected register {0}", DecodeRegister(address)); 107 break; 108 } 109 110 case State.Processing: 111 { 112 parent.NoisyLog("OV2640: Writing 0x{0:X} to register {1}", b, DecodeRegister(address)); 113 RegistersCollection.Write(address, b); 114 break; 115 } 116 117 default: 118 parent.Log(LogLevel.Error, "OV2640: Writing byte in an unexpected state: {0}", state); 119 break; 120 } 121 } 122 DefineDSPRegisters()123 private void DefineDSPRegisters() 124 { 125 DSPRegister.ImageMode.Define(dspRegisters) 126 .WithTag("Byte swap enable for DVP", 0, 1) 127 .WithTag("HREF timing select in DVP JPEG output mode", 1, 1) 128 .WithEnumField<ByteRegister, OutputFormat>(2, 2, out outputFormat, name: "DVP output format") 129 .WithTag("JPEG output enable", 4, 1) 130 .WithReservedBits(5, 1) 131 .WithTag("Y8 enable for DVP", 6, 1) 132 .WithReservedBits(7, 1) 133 ; 134 135 DSPRegister.ZoomOutputHeight.Define(dspRegisters) 136 .WithValueField(0, 8, out zoomOutputHeightLow, name: "OUTH[7:0] (real/4)") 137 ; 138 139 DSPRegister.ZoomOutputWidth.Define(dspRegisters) 140 .WithValueField(0, 8, out zoomOutputWidthLow, name: "OUTW[7:0] (real/4)") 141 ; 142 143 DSPRegister.ZoomOutputRest.Define(dspRegisters) 144 .WithValueField(0, 2, out zoomOutputWidthHigh, name: "OUTW[9:8] (real/4)") 145 .WithValueField(2, 1, out zoomOutputHeightHigh, name: "OUTH[8] (real/4)") 146 .WithReservedBits(3, 1) 147 .WithTag("ZMSPD (zoom speed)", 4, 3) 148 ; 149 150 DefineBankSelectRegister(dspRegisters); 151 } 152 DefineSensorRegisters()153 private void DefineSensorRegisters() 154 { 155 SensorRegister.COM7.Define(sensorRegisters) 156 .WithReservedBits(0, 1) 157 .WithTag("Color bar test pattern", 1, 1) 158 .WithTag("Zoom mode", 2, 1) 159 .WithReservedBits(3, 1) 160 .WithEnumField<ByteRegister, ResolutionMode>(4, 3, out resolution, name: "Resolution selection", 161 writeCallback: (_, val) => parent.Log(LogLevel.Debug, "Resolution set to {0}", val)) 162 .WithFlag(7, name: "SRST", writeCallback: (_, val) => 163 { 164 if(!val) 165 { 166 return; 167 } 168 169 parent.NoisyLog("OV2640: Initiating system reset"); 170 Reset(); 171 }) 172 ; 173 174 DefineBankSelectRegister(sensorRegisters); 175 } 176 DefineBankSelectRegister(ByteRegisterCollection registers)177 private void DefineBankSelectRegister(ByteRegisterCollection registers) 178 { 179 // this register is the same for both banks 180 // so there is no difference which enum we use 181 DSPRegister.RegisterBankSelect.Define(registers) 182 .WithFlag(0, name: "Register bank select", 183 valueProviderCallback: _ => sensorRegisterBankSelected, 184 writeCallback: (_, val) => 185 { 186 sensorRegisterBankSelected = val; 187 parent.NoisyLog("OV2640: Register bank selected: {0}", sensorRegisterBankSelected ? "sensor" : "DSP"); 188 }) 189 .WithReservedBits(1, 7) 190 ; 191 } 192 DecodeRegister(byte address)193 private string DecodeRegister(byte address) 194 { 195 return "{0} (0x{1:X})".FormatWith(sensorRegisterBankSelected 196 ? ((SensorRegister)address).ToString() 197 : ((DSPRegister)address).ToString(), 198 address); 199 } 200 201 private State state; 202 private bool sensorRegisterBankSelected; 203 private byte address; 204 205 private IValueRegisterField zoomOutputHeightHigh; 206 private IValueRegisterField zoomOutputHeightLow; 207 208 private IValueRegisterField zoomOutputWidthHigh; 209 private IValueRegisterField zoomOutputWidthLow; 210 211 private IEnumRegisterField<OutputFormat> outputFormat; 212 private IEnumRegisterField<ResolutionMode> resolution; 213 214 private readonly ByteRegisterCollection dspRegisters; 215 private readonly ByteRegisterCollection sensorRegisters; 216 217 private readonly IPeripheral parent; 218 219 private enum State 220 { 221 Idle, 222 Processing 223 } 224 225 private enum ResolutionMode 226 { 227 UXGA_1600_1200 = 0, 228 CIF_352_288 = 2, 229 SVGA_800_600 = 4 230 } 231 232 public enum OutputFormat 233 { 234 YUV422 = 0, 235 RAW10_DVP = 1, 236 RGB565 = 2, 237 Reserved = 3 238 } 239 240 private enum DSPRegister 241 { 242 // unlisted registers are reserved 243 BypassDSP = 0x5, 244 245 QuantizationScaleFactor = 0x44, 246 247 ControlI = 0x50, 248 HorizontalSizeLow = 0x51, 249 VerticalSizeLow = 0x52, 250 OffsetXLow = 0x53, 251 OffsetYLow = 0x54, 252 SizeOffsetHigh = 0x55, 253 254 DPRP = 0x56, 255 Test = 0x57, 256 257 ZoomOutputWidth = 0x5A, 258 ZoomOutputHeight = 0x5B, 259 ZoomOutputRest = 0x5C, 260 261 SDEIndirectRegisterAccess_Address = 0x7C, 262 SDEIndirectRegisterAccess_Data = 0x7D, 263 264 Control2_ModuleEnable = 0x86, 265 Control3_ModuleEnable = 0x87, 266 267 SIZEL = 0x8C, 268 269 HSIZE8 = 0xC0, 270 VSIZE8 = 0xC1, 271 Control0_ModuleEnable = 0xC2, 272 Control1_ModuleEnable = 0xC3, 273 274 R_DVP_SP = 0xD3, 275 276 ImageMode = 0xDA, 277 278 Reset = 0xE0, 279 280 MS_SP = 0xF0, 281 282 SS_ID = 0xF7, 283 SS_CTRL = 0xF8, 284 MC_BIST = 0xF9, 285 MC_AL = 0xFA, 286 MC_AH = 0xFB, 287 MC_D = 0xFC, 288 P_CMD = 0xFD, 289 P_STATUS = 0xFE, 290 RegisterBankSelect = 0xFF 291 } 292 293 private enum SensorRegister 294 { 295 // unlisted registers are reserved 296 GAIN = 0x00, 297 298 COM1 = 0x3, 299 REG04 = 0x04, 300 301 REG08 = 0x08, 302 COM2 = 0x09, 303 PIDH = 0x0A, 304 PIDL = 0x0B, 305 COM3 = 0x0C, 306 COM4 = 0x0D, 307 308 AEC = 0x10, 309 CLKRC = 0x11, 310 COM7 = 0x12, 311 COM8 = 0x13, 312 COM9 = 0x14, 313 COM10 = 0x15, 314 315 HREFST = 0x17, 316 HREFEND = 0x18, 317 VSTRT = 0x19, 318 VEND = 0x1A, 319 320 MIDH = 0x1C, 321 MIDL = 0x1D, 322 323 AEW = 0x24, 324 AWB = 0x25, 325 VV = 0x26, 326 327 REG2A = 0x2A, 328 FRARL = 0x2B, 329 330 ADDVSL = 0x2D, 331 ADDVSH = 0x2E, 332 YAVG = 0x2F, 333 HSDY = 0x30, 334 HEDY = 0x31, 335 REG32 = 0x32, 336 337 ARCOM2 = 0x34, 338 339 REG45 = 0x45, 340 FLL = 0x46, 341 FLH = 0x47, 342 COM19 = 0x48, 343 ZOOMS = 0x49, 344 345 COM22 = 0x4B, 346 347 COM25 = 0x4E, 348 BD50 = 0x4F, 349 BD60 = 0x50, 350 351 REG5D = 0x5D, 352 REG5E = 0x5E, 353 REG5F = 0x5F, 354 REG60 = 0x60, 355 356 HISTO_LOW = 0x61, 357 HISTO_HIGH = 0x62, 358 359 RegisterBankSelect = 0xFF 360 } 361 } 362 } 363