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) #######################################