/// <summary> /// Send data /// </summary> /// <param name="dataToSend">The data to send</param> /// <returns>An awaitable task that completes upon transmission of the data</returns> public async Task SendAsync(byte[] dataToSend) { if (dataToSend.Length > 63) { throw new Exception("The maximum UART length for one transaction is 63 bytes"); } var data = new byte[dataToSend.Length + 3]; data[0] = (byte)DeviceCommands.UartTransaction; data[1] = (byte)UartCommand.Transmit; data[2] = (byte)dataToSend.Length; dataToSend.CopyTo(data, 3); using (await _device.ComsLock.LockAsync().ConfigureAwait(false)) { await _device.SendPeripheralConfigPacketAsync(data).ConfigureAwait(false); await _device.ReceiveCommsResponsePacketAsync(1).ConfigureAwait(false); } }
/// <summary> /// Send/receive data /// </summary> /// <param name="dataToWrite"> /// a byte array of the data to send. The length of the transaction is determined by the length of this array. /// </param> /// <param name="chipSelect">The chip select pin, if any, to use during this transaction.</param> /// <param name="chipSelectMode">The chip select mode to use during this transaction (if a CS pin is selected)</param> /// <param name="speedMhz">The speed to perform this transaction at.</param> /// <param name="burstMode"The burst mode (if any) to use.</param> /// <param name="spiMode">The SPI mode to use during this transaction.</param> /// <returns>An awaitable byte array with the received data.</returns> public async Task <byte[]> SendReceiveAsync(byte[] dataToWrite, SpiChipSelectPin chipSelect = null, ChipSelectMode chipSelectMode = ChipSelectMode.SpiActiveLow, double speedMhz = 6, SpiBurstMode burstMode = SpiBurstMode.NoBurst, SpiMode spiMode = SpiMode.Mode00) { var transactionLength = dataToWrite.Length; var returnedData = new byte[transactionLength]; if (Enabled != true) { Utility.Error("SPI module must be enabled before starting transaction", true); } if (chipSelect != null && chipSelect.SpiModule != this) { Utility.Error("Chip select pin must belong to this SPI module", true); } if (speedMhz > 0.8 && speedMhz < 6) { Debug.WriteLine( "NOTICE: automatically rounding up SPI speed to 6 MHz, due to a possible silicon bug. This bug affects SPI speeds between 0.8 and 6 MHz, so if you need a speed lower than 6 MHz, please set to 0.8 MHz or lower."); speedMhz = 6; } using (await _device.ComsLock.LockAsync().ConfigureAwait(false)) { var spi0Ckr = (int)Math.Round(24.0 / speedMhz - 1); if (spi0Ckr > 255.0) { spi0Ckr = 255; Debug.WriteLine( "NOTICE: Requested SPI frequency of {0} MHz is below the minimum frequency, and will be clipped to 0.09375 MHz (93.75 kHz).", speedMhz); } else if (spi0Ckr < 0) { spi0Ckr = 0; Debug.WriteLine( "NOTICE: Requested SPI frequency of {0} MHz is above the maximum frequency, and will be clipped to 24 MHz.", speedMhz); } var actualFrequency = 48.0 / (2.0 * (spi0Ckr + 1.0)); if (Math.Abs(actualFrequency - speedMhz) > 1) { Debug.WriteLine( "NOTICE: SPI module actual frequency of {0} MHz is more than 1 MHz away from the requested frequency of {1} MHz", actualFrequency, speedMhz); } if (dataToWrite.Length > 255) { throw new Exception("Maximum packet length is 255 bytes"); } var header = new byte[7]; header[0] = (byte)DeviceCommands.SpiTransaction; header[1] = (byte)(chipSelect?.PinNumber ?? 255); header[2] = (byte)chipSelectMode; header[3] = (byte)spi0Ckr; header[4] = (byte)spiMode; header[5] = (byte)burstMode; header[6] = (byte)transactionLength; // just send the header if (burstMode == SpiBurstMode.BurstRx) { await _device.SendPeripheralConfigPacketAsync(header).ConfigureAwait(false); } else { var dataToSend = new byte[transactionLength + header.Length]; Array.Copy(header, dataToSend, header.Length); Array.Copy(dataToWrite, 0, dataToSend, header.Length, transactionLength); var bytesRemaining = dataToSend.Length; var offset = 0; while (bytesRemaining > 0) { var transferLength = bytesRemaining > 64 ? 64 : bytesRemaining; var tmp = dataToSend.Skip(offset).Take(transferLength); await _device.SendPeripheralConfigPacketAsync(tmp.ToArray()).ConfigureAwait(false); offset += transferLength; bytesRemaining -= transferLength; } } // no need to wait if we're not reading anything if (burstMode != SpiBurstMode.BurstTx) { var bytesRemaining = transactionLength; var srcIndex = 0; while (bytesRemaining > 0) { var numBytesToTransfer = bytesRemaining > 64 ? 64 : bytesRemaining; var receivedData = await _device.ReceiveCommsResponsePacketAsync((uint)numBytesToTransfer) .ConfigureAwait(false); Array.Copy(receivedData, 0, returnedData, srcIndex, receivedData.Length); // just in case we don't get what we're expecting srcIndex += numBytesToTransfer; bytesRemaining -= numBytesToTransfer; } } } return(returnedData); }
/// <summary> /// Sends and Receives data. /// </summary> /// <param name="address">The 7-bit address of the device. This address should not include the read/write bit.</param> /// <param name="dataToWrite">Array of one or more bytes to write to the device.</param> /// <param name="numBytesToRead">Number of bytes to receive from the device.</param> /// <returns>Data read from the device.</returns> /** * Reading and writing data occurs according to dataToWrite and numBytesToRead. * * The board will write the 7-bit address. If dataToWrite is set, the "read" bit is cleared to indicate a "write" transaction, and %Treehopper will write dataToWrite to the board. If numBytesToRead is 0, a stop condition will be sent. But if numBytesToRead is not 0, a restart condition will be sent, followed by the address and "read" bit. %Treehopper will then read numBytesToRead bytes from the device. * * On the other hand, if dataToWrite is null, the board will write the 7-bit address, setting the "read" bit and reading out numBytesToRead bytes. * * By supporting both null dataToWrite and numBytesToRead=0 conditions, this function can be used for all standard I2C/SMBus transactions. * * Most %I2C devices use a register-based scheme for exchanging data; consider using Treehopper.Libraries.SMBusDevice for interacting with these devices. */ public async Task <byte[]> SendReceiveAsync(byte address, byte[] dataToWrite, byte numBytesToRead) { if (!Enabled) { Debug.WriteLine( "NOTICE: I2c.SendReceive() called before enabling the peripheral. This call will be ignored."); } var receivedData = new byte[numBytesToRead]; var txLen = dataToWrite?.Length ?? 0; using (await _device.ComsLock.LockAsync().ConfigureAwait(false)) { var dataToSend = new byte[4 + txLen]; // 2 bytes for the header dataToSend[0] = (byte)DeviceCommands.I2cTransaction; dataToSend[1] = address; dataToSend[2] = (byte)txLen; // total length (0-255) dataToSend[3] = numBytesToRead; if (txLen > 0) { Array.Copy(dataToWrite, 0, dataToSend, 4, txLen); } var bytesRemaining = dataToSend.Length; var offset = 0; // for long transactions (> 64 bytes - 4 byte header), we send <=64 byte chunks, one by one. while (bytesRemaining > 0) { var transferLength = bytesRemaining > 64 ? 64 : bytesRemaining; var tmp = dataToSend.Skip(offset).Take(transferLength); await _device.SendPeripheralConfigPacketAsync(tmp.ToArray()).ConfigureAwait(false); offset += transferLength; bytesRemaining -= transferLength; } if (numBytesToRead == 0) { var result = await _device.ReceiveCommsResponsePacketAsync(1).ConfigureAwait(false); if (result[0] != 255) { var error = (I2CTransferError)result[0]; Debug.WriteLine("NOTICE: I2C transaction resulted in an error: " + error); if (TreehopperUsb.Settings.ThrowExceptions) { throw new I2CTransferException { Error = error } } ; } } else { bytesRemaining = numBytesToRead + 1; // received data length + status byte var srcIndex = 0; var result = new byte[bytesRemaining]; while (bytesRemaining > 0) { var numBytesToTransfer = bytesRemaining > 64 ? 64 : bytesRemaining; var chunk = await _device.ReceiveCommsResponsePacketAsync((uint)numBytesToTransfer).ConfigureAwait(false); Array.Copy(chunk, 0, result, srcIndex, chunk.Length); // just in case we don't get what we're expecting srcIndex += numBytesToTransfer; bytesRemaining -= numBytesToTransfer; } if (result[0] != 255) { var error = (I2CTransferError)result[0]; Debug.WriteLine("NOTICE: I2C transaction resulted in an error: " + error); if (TreehopperUsb.Settings.ThrowExceptions) { throw new I2CTransferException { Error = error } } ; } else { Array.Copy(result, 1, receivedData, 0, numBytesToRead); } } } return(receivedData); }