2019/4/24 追記:packetToBytes()を修正
NCOの周波数設定にアンドロイドアプリ「GUI Maker for Avalon Bus – FPGA SPI Bridge Panel」を使っていますが、ESP32だけで周波数と音量を変更できたほうが便利です。
ESP32単体でFPGA内のAvalonバスを操作できるAvalonPacketライブラリを作りました。QsysにSPI Slave to Avalon Master Bridgeを追加すれば、Nios II/e無しでPIOなどを操作できます。
下記スケッチは電源投入時、音量と周波数をセットしています。さらにESP32のタッチセンサーを使えば音量と周波数を変更する機能も追加可能です。
#include <SPI.h> #include <AvalonPacket.h> AvalonPacket ap = AvalonPacket(); byte response[4]; int responseSize; void setup() { Serial.begin(57600); delay(1000); // wait until FPGA is configured sendVolume(10); sendFreq(80000000); } void loop() { } void sendVolume(unsigned long volume) { // 0 - 15 responseSize = ap.write(0, volume, response); ap.printBytes(response, responseSize); } void sendFreq(unsigned long freq) { // in Hz unsigned long refFreq = 1000000; // 1MHz unsigned long refValue = 58236845; // NCO phase increment if (freq >= 73750000) // 73.75MHz freq -= 73750000; unsigned long data = (unsigned long)((double)freq / (double)refFreq * (double)refValue); responseSize = ap.write(0x10, data, response); ap.printBytes(response, responseSize); }
まだFPGAラジオで使用するメソッドしか動作確認していませんが、AvalonPacketライブラリを掲載しておきます。
Arduino \ libraries \ AvalonPacket \ AvalonPacket.h
#ifndef _AVALON_PACKET_H #define _AVALON_PACKET_H #include "arduino.h" #include <SPI.h> class AvalonPacket { public: AvalonPacket(); void printBytes(byte *bytes, int bytesSize); int write(unsigned long addr, byte *array, int arraySize, bool isIncremental, byte *response); int write(unsigned long addr, byte data, byte *response); int write(unsigned long addr, unsigned int data, byte *response); int write(unsigned long addr, unsigned long data, byte *response); int write(unsigned long addr, unsigned int *array, int arraySize, bool isIncremental, byte *response); int write(unsigned long addr, unsigned long *array, int arraySize, bool isIncremental, byte *response); int read(unsigned long addr, byte *array, int arraySize, bool isIncremental); int read(unsigned long addr, unsigned int *array, int arraySize, bool isIncremental); int read(unsigned long addr, unsigned long *array, int arraySize, bool isIncremental); byte readByte(unsigned long addr); unsigned int readUInt(unsigned long addr); unsigned long readULong(unsigned long addr); private: bool byteCh = false; bool bytesEscape = false; bool bitsEscape = false; int packetToBytes(byte *packet, int packetSize, byte *bytes); int bytesToPacket(byte *bytes, int bytesSize, byte *packet); int bytesToBits(byte *bytes, int bytesSize, byte *bits); int bitsToBytes(byte *bits, int bitsSize, byte *bytes); int bitsToBytes(byte *bits, int bitsStart, int bitsLength, byte *bytes); int receiveResponse(int requestSize, byte *response); }; #endif
Arduino \ libraries \ AvalonPacket \ AvalonPacket.cpp
#include "arduino.h" #include "AvalonPacket.h" #include <SPI.h> AvalonPacket::AvalonPacket() { SPI.begin(); SPI.setFrequency(24000000); SPI.setDataMode(SPI_MODE1); SPI.setBitOrder(MSBFIRST); pinMode(SS, OUTPUT); digitalWrite(SS, HIGH); } void AvalonPacket::printBytes(byte *bytes, int bytesSize) { Serial.println(); for (int i = 0; i < bytesSize; i++) { Serial.print(bytes[i], HEX); Serial.print(" "); } } int AvalonPacket::write(unsigned long addr, byte *array, int arraySize, bool isIncremental, byte *response) { int packetSize = 8 + arraySize; byte packet[packetSize]; byte sizeBytes[2] = { arraySize & 0xff, (arraySize >> 8) & 0xff }; byte addrBytes[4] = { addr & 0xff, (addr >> 8) & 0xff, (addr >> 16) & 0xff, (addr >> 24) & 0xff }; packet[0] = (byte)(isIncremental ? 0x04 : 0x00); packet[1] = 0; packet[2] = sizeBytes[1]; packet[3] = sizeBytes[0]; packet[4] = addrBytes[3]; packet[5] = addrBytes[2]; packet[6] = addrBytes[1]; packet[7] = addrBytes[0]; for (int i = 0; i < arraySize; i++) { packet[8 + i] = array[i]; } byte bytes[packetSize * 2]; int bytesSize = packetToBytes(packet, packetSize, bytes); byte bits[bytesSize * 2]; int bitsSize = bytesToBits(bytes, bytesSize, bits); byte spiReadBuf[bitsSize]; digitalWrite(SS, LOW); SPI.transferBytes(bits, spiReadBuf, bitsSize); digitalWrite(SS, HIGH); return receiveResponse(4, response); } int AvalonPacket::write(unsigned long addr, byte data, byte *response) { byte dataBytes[1] = { data }; return write(addr, dataBytes, 1, false, response); } int AvalonPacket::write(unsigned long addr, unsigned int data, byte *response) { byte dataBytes[2] = { data & 0xff, (data >> 8) & 0xff }; return write(addr, dataBytes, 2, false, response); } int AvalonPacket::write(unsigned long addr, unsigned long data, byte *response) { byte dataBytes[4] = { data & 0xff, (data >> 8) & 0xff, (data >> 16) & 0xff, (data >> 24) & 0xff }; return write(addr, dataBytes, 4, false, response); } int AvalonPacket::write(unsigned long addr, unsigned int *array, int arraySize, bool isIncremental, byte *response) { int numBytes = 2; byte bytes[numBytes * arraySize]; for (int i = 0; i < arraySize; i++) { bytes[numBytes * i + 0] = array[i] & 0xff; bytes[numBytes * i + 1] = (array[i] >> 8) & 0xff; } return write(addr, bytes, numBytes * arraySize, isIncremental, response); } int AvalonPacket::write(unsigned long addr, unsigned long *array, int arraySize, bool isIncremental, byte *response) { int numBytes = 4; byte bytes[numBytes * arraySize]; for (int i = 0; i < arraySize; i++) { bytes[numBytes * i + 0] = array[i] & 0xff; bytes[numBytes * i + 1] = (array[i] >> 8) & 0xff; bytes[numBytes * i + 2] = (array[i] >> 16) & 0xff; bytes[numBytes * i + 3] = (array[i] >> 24) & 0xff; } return write(addr, bytes, numBytes * arraySize, isIncremental, response); } int AvalonPacket::read(unsigned long addr, byte *array, int arraySize, bool isIncremental) { if (arraySize == 0) return 0; int packetSize = 8; byte packet[packetSize]; byte sizeBytes[2] = { arraySize & 0xff, (arraySize >> 8) & 0xff }; byte addrBytes[4] = { addr & 0xff, (addr >> 8) & 0xff, (addr >> 16) & 0xff, (addr >> 24) & 0xff }; packet[0] = (byte)(isIncremental ? 0x14 : 0x10); packet[1] = 0; packet[2] = sizeBytes[1]; packet[3] = sizeBytes[0]; packet[4] = addrBytes[3]; packet[5] = addrBytes[2]; packet[6] = addrBytes[1]; packet[7] = addrBytes[0]; byte bytes[packetSize * 2]; int bytesSize = packetToBytes(packet, packetSize, bytes); byte bits[bytesSize * 2]; int bitsSize = bytesToBits(bytes, bytesSize, bits); byte spiReadBuf[bitsSize]; digitalWrite(SS, LOW); SPI.transferBytes(bits, spiReadBuf, bitsSize); digitalWrite(SS, HIGH); return receiveResponse(arraySize, array); } int AvalonPacket::read(unsigned long addr, unsigned int *array, int arraySize, bool isIncremental) { if (arraySize == 0) return 0; int numBytes = 2; byte bytes[numBytes * arraySize]; int bytesSize = read(addr, bytes, numBytes * arraySize, isIncremental); for (int i = 0; i < arraySize; i++) { array[i] = bytes[numBytes * i + 0] + (bytes[numBytes * i + 1] << 8); } return arraySize; } int AvalonPacket::read(unsigned long addr, unsigned long *array, int arraySize, bool isIncremental) { if (arraySize == 0) return 0; int numBytes = 4; byte bytes[numBytes * arraySize]; int bytesSize = read(addr, bytes, numBytes * arraySize, isIncremental); for (int i = 0; i < arraySize; i++) { array[i] = bytes[numBytes * i + 0] + (bytes[numBytes * i + 1] << 8) + (bytes[numBytes * i + 2] << 16) + (bytes[numBytes * i + 3] << 24); } return arraySize; } byte AvalonPacket::readByte(unsigned long addr) { byte array[1]; int arraySize = read(addr, array, 1, false); if (arraySize == 0) return 0; return array[0]; } unsigned int AvalonPacket::readUInt(unsigned long addr) { unsigned int array[1]; int arraySize = read(addr, array, 1, false); if (arraySize == 0) return 0; return array[0]; } unsigned long AvalonPacket::readULong(unsigned long addr) { unsigned long array[1]; int arraySize = read(addr, array, 1, false); if (arraySize == 0) return 0; return array[0]; } int AvalonPacket::packetToBytes(byte *packet, int packetSize, byte *bytes) { int bytesSize = 0; bytes[bytesSize++] = 0x7c; bytes[bytesSize++] = 0; bytes[bytesSize++] = 0x7a; for (int i = 0; i < packetSize; i++) { byte p = packet[i]; if (0x7a <= p && p <= 0x7d) { if (i == packetSize - 1) { bytes[bytesSize++] = 0x7b; } bytes[bytesSize++] = 0x7d; bytes[bytesSize++] = (byte)(p ^ 0x20); } else { if (i == packetSize - 1) { bytes[bytesSize++] = 0x7b; } bytes[bytesSize++] = p; } } return bytesSize; } int AvalonPacket::bytesToPacket(byte *bytes, int bytesSize, byte *packet) { int packetSize = 0; for (int i = 0; i < bytesSize; i++) { byte b = bytes[i]; if (b == 0x7a || b == 0x7b) { // Dropped } else if (b == 0x7c) { byteCh = true; // Dropped } else if (b == 0x7d) { bytesEscape = true; // Dropped } else { if (byteCh) { byteCh = false; // Dropped } else if (bytesEscape) { bytesEscape = false; packet[packetSize++] = (byte)(b ^ 0x20); } else { packet[packetSize++] = b; } } } return packetSize; } int AvalonPacket::bytesToBits(byte *bytes, int bytesSize, byte *bits) { int bitsSize = 0; for (int i = 0; i < bytesSize; i++) { byte b = bytes[i]; if (b == 0x4a || b == 0x4d) { bits[bitsSize++] = 0x4d; bits[bitsSize++] = (byte)(b ^ 0x20); } else { bits[bitsSize++] = b; } } return bitsSize; } int AvalonPacket::bitsToBytes(byte *bits, int bitsSize, byte *bytes) { return bitsToBytes(bits, 0, bitsSize, bytes); } int AvalonPacket::bitsToBytes(byte *bits, int bitsStart, int bitsLength, byte *bytes) { int bytesSize = 0; for (int i = bitsStart; i < bitsStart + bitsLength; i++) { byte b = bits[i]; if (b == 0x4a) { // Dropped } else if (b == 0x4d) { bitsEscape = true; // Dropped } else { if (bitsEscape) { bitsEscape = false; bytes[bytesSize++] = (byte)(b ^ 0x20); } else { bytes[bytesSize++] = b; } } } return bytesSize; } int AvalonPacket::receiveResponse(int requestSize, byte *response) { byteCh = false; bytesEscape = false; bitsEscape = false; int responseSize = 0; while (responseSize < requestSize) { byte bits[requestSize]; for (int i = 0; i < requestSize; i++) { bits[i] = 0x4a; } byte spiReadBuf[requestSize]; digitalWrite(SS, LOW); SPI.transferBytes(bits, spiReadBuf, requestSize); digitalWrite(SS, HIGH); byte bytes[requestSize]; int bytesSize = bitsToBytes(spiReadBuf, requestSize, bytes); byte packet[requestSize]; int packetSize = bytesToPacket(bytes, bytesSize, packet); for (int i = 0; i < packetSize; i++) { response[responseSize++] = packet[i]; } } return responseSize; }
Arduino \ libraries \ AvalonPacket \ keywords.txt
####################################### # Datatypes (KEYWORD1) ####################################### AvalonPacket KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### printBytes KEYWORD2 write KEYWORD2 read KEYWORD2 readByte KEYWORD2 readUInt KEYWORD2 readULong KEYWORD2 ####################################### # Constants (LITERAL1) #######################################