FPGA SDR(25)タッチセンサーで選局、音量調節


2018/12/15 追記:タッチセンサーの処理を修正しました。
2018/07/15 追記:NVSに選局、音量調節の状態を保持するようにしました。

 

ESP32単体でFPGA内のAvalonバスを操作できるAvalonPacketライブラリを作ったので、ESP32のタッチセンサーを使って選局、音量調節をできるようにしました。ESP32から4本の配線(緑色)をプラケースの外に引き出しただけです。メカスイッチだといつも固定方法に悩みますがタッチセンサーは楽ちんです。

 

使っているESP32は、10本のタッチセンサーT0~T9の内、T1、T2、T5がほかの用途に使われているためタッチセンサーとしては機能しませんでした。FPGAラジオには選局にT4、T6を、音量調節にT8、T9を使っています。

選局は、周波数からNCOの設定値を計算してFPGA内のAvalonバス上のPIO(アドレス0x10)に書き込みます。

音量調節は、PIO(アドレス0x0)に書き込んでI2S DACの出力レベルを操作します。

#include <SPI.h>
#include <AvalonPacket.h>
#include <Wire.h>
#include "SSD1306Wire.h"
#include <nvs.h>

uint8_t volume = 0;
uint8_t station = 0;
const int NUM_STATIONS = 10;
unsigned long freq[NUM_STATIONS] = {
  594000,
  810000,
  954000,
  1134000,
  80000000,
  81300000,
  82500000,
  90500000,
  91600000,
  93000000
};
String freqString[NUM_STATIONS] = {
  "594 kHz",
  "810 kHz",
  "954 kHz",
  "1134 kHz",
  "80.0 MHz",
  "81.3 MHz",
  "82.5 MHz",
  "90.5 MHz",
  "91.6 MHz",
  "93.0 MHz"
};
String stationName[NUM_STATIONS] = {
  "NHK",
  "AFN",
  "TBS",
  "JOQR",
  "TOKYO FM",
  "J-WAVE",
  "NHK FM",
  "TBS FM",
  "JOQR FM",
  "NIPPON FM"
};

SSD1306Wire display(0x3c, 21, 22);
void printBytes(byte *bytes, int bytesSize);

nvs_handle handle = 0;;

AvalonPacket ap = AvalonPacket();
byte response[4];
int responseSize;

int threshold = 60;
volatile unsigned long touchMillis = 0;
volatile int touchId = 0;

void gotTouch(int id) {
  touchAttachInterrupt(T4, dummy, 0);
  touchAttachInterrupt(T6, dummy, 0);
  touchAttachInterrupt(T8, dummy, 0);
  touchAttachInterrupt(T9, dummy, 0);
  touchMillis = millis();
  touchId = id;
}

void dummy() { }

void gotTouch1() {
  if (touchRead(T4) < threshold) {
    gotTouch(1);
  }
}

void gotTouch2() {
  if (touchRead(T6) < threshold) {
    gotTouch(2);
  }
}

void gotTouch3() {
  if (touchRead(T8) < threshold) {
    gotTouch(3);
  }
}

void gotTouch4() {
  if (touchRead(T9) < threshold) {
    gotTouch(4);
  }
}

void setup() {
  Serial.begin(57600);

  display.init();
  display.flipScreenVertically();

  nvs_open("FPGA Radio", NVS_READWRITE, &handle);
  if (handle != 0) {
    nvs_get_u8(handle, "volume", &volume);
    nvs_get_u8(handle, "station", &station);
  }
  if (15 < volume) volume = 15;
  if (NUM_STATIONS-1 < station) station = NUM_STATIONS-1;

  delay(1000); // Wait until FPGA is configured

  sendVolume(volume);
  sendFreq(freq[station]);
  updateDisplay();

  touchAttachInterrupt(T4, gotTouch1, threshold);
  touchAttachInterrupt(T6, gotTouch2, threshold);
  touchAttachInterrupt(T8, gotTouch3, threshold);
  touchAttachInterrupt(T9, gotTouch4, threshold);
}

void loop() {
  if (touchId > 0) {
    Serial.print("Touch: ");
    Serial.println(touchId);

    if (touchId == 1) {
      if (station > 0) {
        station -= 1;
      } else {
        station = NUM_STATIONS-1;
      }
    } else if (touchId == 2) {
      if (station < NUM_STATIONS-1) {
        station += 1;
      } else {
        station = 0;
      }
    } else if (touchId == 3) {
      if (volume > 0) {
        volume -= 1;
      }
    } else if (touchId == 4) {
      if (volume < 15) {
        volume += 1;
      }
    }
    
    sendFreq(freq[station]);
    sendVolume(volume);
    if (handle != 0) {
      nvs_set_u8(handle, "station", station);
      nvs_set_u8(handle, "volume", volume);
    }
    updateDisplay();

    touchId = -1;
  } else if (touchId < 0 && touchMillis + 300 < millis()) {
    touchId = 0;
    touchAttachInterrupt(T4, gotTouch1, threshold);
    touchAttachInterrupt(T6, gotTouch2, threshold);
    touchAttachInterrupt(T8, gotTouch3, threshold);
    touchAttachInterrupt(T9, gotTouch4, threshold);
  }
}

void updateDisplay() {
  display.clear();
  
  display.setFont(ArialMT_Plain_24);
  display.drawString(0, 0, stationName[station]);
  display.drawString(0, 24, freqString[station]);
  
  display.setFont(ArialMT_Plain_24);
  String str = "";
  for (int i = 0; i < volume; i++) {
    str += "*";
  }
  display.drawString(0, 48, str);
  
  display.display();
}

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);
}