FPGA SDR(24)Arduino AvalonPacket Library


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