2020年5月3日日曜日

Attiny85 VFO PCB版

Attiny85 VFO用にPCBを作った。モノバンド用に使いやすくする為、余分なスイッチは無い。今回、電源電圧を3.3Vで動作出来るように、LCDも見直した。si5351aモジュールは、Adafurit製もしくは秋月電子製も動作可能。





回路図

設定

デバイスの設定
       

ヒューズビット

スケッチ

電源電圧3.3Vに対応させるため、LCDをAQM1602XA-RN-GBWに変更した。それに伴い変更している。ファイルはJA2GQP's Download siteのattiny85フォルダからダウンロード可能。

1)インクルードファイル(si5351a21.h)
////////////////////////////////////////////////////////////////////////
// Author: Hans Summers, 2015
// Website: http://www.hanssummers.com
//
// A very very simple Si5351a demonstration
// using the Si5351a module kit http://www.hanssummers.com/synth
// Please also refer to SiLabs AN619 which describes all the registers to use
//----------------------------------------------------------------------
// Modifications: JA2GQP,2017/5/20
//     1)Output is CLK0 and CLK2.
//     2)Arduino and stm32duino Operable. 
////////////////////////////////////////////////////////////////////////

#include <Wire.h>

#define OUTE_CTRL   3
#define CLK0_CTRL   16               // Register definitions
#define CLK1_CTRL   17
#define CLK2_CTRL   18
#define MSNA_ADDR   26
#define MSNB_ADDR   34
#define MS0_ADDR    42
#define MS1_ADDR    50
#define MS2_ADDR    58
#define PLL_RESET   177
#define XTAL_LOAD_C 183

#define R_DIV_1      0b00000000     // R-division ratio definitions
#define R_DIV_2      0b00010000
#define R_DIV_4      0b00100000
#define R_DIV_8      0b00110000
#define R_DIV_16     0b01000000
#define R_DIV_32     0b01010000
#define R_DIV_64     0b01100000
#define R_DIV_128    0b01110000

#define Si5351A_ADDR  0x60

#define CLK_SRC_PLL_A 0b00000000
#define CLK_SRC_PLL_B 0b00100000

#define XTAL_FREQ     25000000    // Crystal frequency for Hans' board

//------------- momory define ------------

uint32_t xtalFreq = XTAL_FREQ;    // 2017/9/29

////////////////////////////////////////////////////////////////////////
// I2C write
////////////////////////////////////////////////////////////////////////

void Si5351_write(byte Reg , byte Data){
  Wire.beginTransmission(Si5351A_ADDR);
  Wire.write(Reg);
  Wire.write(Data);
  Wire.endTransmission();
}

////////////////////////////////////////////////////////////////////////
// Set up specified PLL with mult, num and denom
// mult is 15..90
// num is 0..1,048,575 (0xFFFFF)
// denom is 0..1,048,575 (0xFFFFF)
///////////////////////////////////////////////////////////////////////

void setupPLL(uint8_t pll, uint8_t mult, uint32_t num, uint32_t denom){
  uint32_t P1;                            // PLL config register P1
  uint32_t P2;                            // PLL config register P2
  uint32_t P3;                            // PLL config register P3

  P1 = (uint32_t)(128 * ((float)num / (float)denom));
  P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
  P2 = (uint32_t)(128 * ((float)num / (float)denom));
  P2 = (uint32_t)(128 * num - denom * P2);
  P3 = denom;

  Si5351_write(pll + 0, (P3 & 0x0000FF00) >> 8);
  Si5351_write(pll + 1, (P3 & 0x000000FF));
  Si5351_write(pll + 2, (P1 & 0x00030000) >> 16);
  Si5351_write(pll + 3, (P1 & 0x0000FF00) >> 8);
  Si5351_write(pll + 4, (P1 & 0x000000FF));
  Si5351_write(pll + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
  Si5351_write(pll + 6, (P2 & 0x0000FF00) >> 8);
  Si5351_write(pll + 7, (P2 & 0x000000FF));
}

////////////////////////////////////////////////////////////////////////
// Set up MultiSynth with integer divider and R divider
// R divider is the bit value which is OR'ed onto the appropriate
// register, it is a #define in si5351a.h
////////////////////////////////////////////////////////////////////////

void setupMultisynth(uint8_t synth, uint32_t divider, uint8_t rDiv){
  uint32_t P1;                          // Synth config register P1
  uint32_t P2;                          // Synth config register P2
  uint32_t P3;                          // Synth config register P3

  P1 = 128 * divider - 512;
  P2 = 0;                               // P2 = 0, P3 = 1 forces an integer value for the divider
  P3 = 1;

  Si5351_write(synth + 0, (P3 & 0x0000FF00) >> 8);
  Si5351_write(synth + 1, (P3 & 0x000000FF));
  Si5351_write(synth + 2, ((P1 & 0x00030000) >> 16) | rDiv);
  Si5351_write(synth + 3, (P1 & 0x0000FF00) >> 8);
  Si5351_write(synth + 4, (P1 & 0x000000FF));
  Si5351_write(synth + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
  Si5351_write(synth + 6, (P2 & 0x0000FF00) >> 8);
  Si5351_write(synth + 7, (P2 & 0x000000FF));
}

////////////////////////////////////////////////////////////////////////
// output enable/disable control
// Example: si5351a_enable(0x01);
// CLK0 output disable
////////////////////////////////////////////////////////////////////////

void si5351a_enable(byte oeb){
  Si5351_write(OUTE_CTRL,oeb);                  // AN619 Register 3
}

////////////////////////////////////////////////////////////////////////
// Set CLK0 output ON and to the specified frequency
// Frequency is in the range 1MHz to 150MHz
// Example: si5351aSetFrequency(10000000);
// will set output CLK0 to 10MHz
//
// This example sets up PLL A
// and MultiSynth 0
// and produces the output on CLK0
////////////////////////////////////////////////////////////////////////

void si5351aSetFrequency(uint32_t frequency){
  uint32_t pllFreq;
//  uint32_t xtalFreq = XTAL_FREQ;        // 2017/9/29
  uint32_t l;
  float f;
  uint8_t mult;
  uint32_t num;
  uint32_t denom;
  uint32_t divider;

  divider = 900000000 / frequency;        // Calculate the division ratio. 900,000,000 is the maximum internal
                                          // PLL frequency: 900MHz
  if (divider % 2) divider--;             // Ensure an even integer
                                          //division ratio

  pllFreq = divider * frequency;          // Calculate the pllFrequency:
                                          //the divider * desired output frequency

  mult = pllFreq / xtalFreq;              // Determine the multiplier to
                                          //get to the required pllFrequency
  l = pllFreq % xtalFreq;                 // It has three parts:
  f = l;                                  // mult is an integer that must be in the range 15..90
  f *= 1048575;                           // num and denom are the fractional parts, the numerator and denominator
  f /= xtalFreq;                          // each is 20 bits (range 0..1048575)
  num = f;                                // the actual multiplier is mult + num / denom
  denom = 1048575;                        // For simplicity we set the denominator to the maximum 1048575

                                          // Set up PLL A with the calculated  multiplication ratio
  setupPLL(MSNA_ADDR, mult, num, denom);
                                          // Set up MultiSynth divider 0, with the calculated divider.
                                          // The final R division stage can divide by a power of two, from 1..128.
                                          // reprented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
                                          // If you want to output frequencies below 1MHz, you have to use the
                                          // final R division stage
  setupMultisynth(MS0_ADDR, divider, R_DIV_1);
                                          // Reset the PLL. This causes a glitch in the output. For small changes to
                                          // the parameters, you don't need to reset the PLL, and there is no glitch
//  Si5351_write(PLL_RESET, 0x20);
                                          // Finally switch on the CLK0 output (0x4F)
                                          // and set the MultiSynth0 input to be PLL A
  Si5351_write(CLK0_CTRL, 0x4F | CLK_SRC_PLL_A);    // Strength 8mA
}

////////////////////////////////////////////////////////////////////////
// Set CLK1 output ON and to the specified frequency
// Frequency is in the range 1MHz to 150MHz
// Example: si5351aSetFrequency2(10000000);
// will set output CLK0 to 10MHz
//
// This example sets up PLL B
// and MultiSynth 1
// and produces the output on CLK1
////////////////////////////////////////////////////////////////////////

void si5351aSetFrequency2(uint32_t frequency){
  uint32_t pllFreq;
//  uint32_t xtalFreq = XTAL_FREQ;        // 2017/9/29
  uint32_t l;
  float f;
  uint8_t mult;
  uint32_t num;
  uint32_t denom;
  uint32_t divider;

  divider = 900000000 / frequency;        // Calculate the division ratio. 900,000,000 is the maximum internal
                                          // PLL frequency: 900MHz
  if (divider % 2) divider--;             // Ensure an even integer
                                          //division ratio

  pllFreq = divider * frequency;          // Calculate the pllFrequency:
                                          //the divider * desired output frequency

  mult = pllFreq / xtalFreq;              // Determine the multiplier to
                                          //get to the required pllFrequency
  l = pllFreq % xtalFreq;                 // It has three parts:
  f = l;                                  // mult is an integer that must be in the range 15..90
  f *= 1048575;                           // num and denom are the fractional parts, the numerator and denominator
  f /= xtalFreq;                          // each is 20 bits (range 0..1048575)
  num = f;                                // the actual multiplier is mult + num / denom
  denom = 1048575;                        // For simplicity we set the denominator to the maximum 1048575

                                          // Set up PLL B with the calculated  multiplication ratio
  setupPLL(MSNB_ADDR, mult, num, denom);
                                          // Set up MultiSynth divider 0, with the calculated divider.
                                          // The final R division stage can divide by a power of two, from 1..128.
                                          // reprented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
                                          // If you want to output frequencies below 1MHz, you have to use the
                                          // final R division stage
  setupMultisynth(MS2_ADDR, divider, R_DIV_1);
                                          // Reset the PLL. This causes a glitch in the output. For small changes to
                                          // the parameters, you don't need to reset the PLL, and there is no glitch
//  Si5351_write(PLL_RESET, 0x80);
                                          // Finally switch on the CLK1 output
                                          // and set the MultiSynth0 input to be PLL B
  Si5351_write(CLK2_CTRL, 0x6F | CLK_SRC_PLL_B);    // Strength 8mA
}


2)VFOスケッチ(st7032_vfo)
///////////////////////////////////////////////////////////////////////////////////
//    si5351a VFO Atiny85
//
//                                                        2020/3/23
//                                                        JA2GQP   
//---------------------------------------------------------------------------------
//    fuse BIT
//                low = f1 , high = DF , extend = ff
//
///////////////////////////////////////////////////////////////////////////////////

//---------- Library include --------------------------------

#include "src/si5351a21.h"                   
#include "src/Rotary.h"
#include "src/ST7032.h"
#include <EEPROM.h>

//---------- Define value -----------------------------------

#define SW1 (val < 682) && ( val >= 472)  // SW1
#define SW2 (val < 767) && ( val >= 642)  // SW2
#define SW3 (val < 818) && ( val >= 727)  // SW3

#define SW_TX   SW1
#define SW_RIT  SW2
#define ENC_A   PB1
#define ENC_B   PB3
             
Rotary r=Rotary(ENC_B,ENC_A);
ST7032 lcd;

////////////////////////////////
// EEPROM Memory Address
////////////////////////////////
const byte  Eep_Init = 0x00;        // Eep Init(1byte*1)
const byte  Eep_Band = 0x01;        // Band(1byte*1)
const byte  Eep_Freq = 0x10;        // Frequency(4byte*8)
const byte  Eep_Step = 0x30;        // STEP(4byte*8)

////////////////////////////////
// frquency data
////////////////////////////////
const unsigned long FRQ_TBL[4] = {
 // DEF     LOW(cw)  MID(ssb)  HI
  7050000 ,7000000 ,7045000 ,7200000
  };

//---- data offset -----
const byte DEF_F = 0;
const byte LOW_F = 1;
const byte MID_F = 2;
const byte HI_F  = 3;

//---- Bfo data -----
const unsigned long IF_FREQ = 10700000L; 
const unsigned long CW =      10700600L; 
const unsigned long LSB =     10701500L;
const unsigned long USB =     10698500L;

////////////////////////////////
// etc
////////////////////////////////
const byte  Int_End = 73;           // Initial end code
const int  LW_RIT = -5000;          // RIT Lower Limit
const int  HI_RIT = 5000;           // RIT Upper Limit

byte bar5[8]={B00000,               // s-meter barcode data
B11011,
B11011,
B11011,
B11011,
B11011,
B11011,
B00000};

byte bar1[8]={B10000,
B11000,
B11100,
B11110,
B11110,
B11100,
B11000,
B10000};

unsigned long previousMillis;

//---------- Variable setting -------------------------------

unsigned long Vfo_Dat;
unsigned long Vfo_Datb = 0;
unsigned long Bfo_Dat;
unsigned long Bfo_Datb = 0;
unsigned long Enc_Step = 10;

int Rit_Dat;                        // RIT Data
int Rit_Datb = 0;                   //          old
long Lng_Wk;                        // Long Work
int  Int_Wk;                        // Int Work
char Lcd_Dat[12] = "           ";   // Lcd Display Buffer
byte Flg_Tx = 0;                    // TX Flag
byte Flg_Mode = 0;                  // Mode Flag
byte Flg_Over = 0;
byte Flg_Rit = 0;                   // RIT Flag
byte Flg_Rdisp = 0;                 // RIT Display Flag
byte Flg_Disp = 0;                  // Display Flag

long Enc_Delay = 0;                 // Encorder Delay(ms)
long Time_Passd;                    // Time Pass(ms)
byte Flg_eepWT = 0;                 // EEP Write Flag

long Enc_Velocityb;
long Enc_Velocity;                  // Encorder velocity(ms)

//----------  Initialization  Program  ----------------------

void setup() {
  unsigned int val;

  pinMode(ENC_A,INPUT_PULLUP);      // PB3
  pinMode(ENC_B,INPUT_PULLUP);      // PB4

  lcd.begin(16, 2);
  lcd.setContrast(40);            // at 3.3V
//  lcd.setContrast(10);              // at 5V

  lcd.createChar(0, bar5);
  lcd.createChar(1, bar1); 

  GIMSK |= (1 << PCIE);             // Enable pin change interrupt
  PCMSK |= (1 << PCINT1) | (1 << PCINT3);
  sei();                            // INT Enable

  val = analogRead(A0);             // EEPROM init check
  delay(1);
  if(SW_RIT)                        // init?
    EEPROM.write(Eep_Init,0x00);     
  do{
    val = analogRead(A0);         
    delay(1);
    if(SW_RIT){
      lcd.setCursor(0, 0);
      lcd.print("EEP Initialize");
    }
  }
  while(SW_RIT);

  if(EEPROM.read(Eep_Init) != Int_End){ 
    delay(10);
    eep_init();
  }

  eep_rdata();
  delay(10);

  band_check();
  mode_disp();
}

//----------  Main program  ---------------------------------

void loop() {
  unsigned int val;

  val = analogRead(A0);
  delay(1);
  if(SW_TX)                         // SW_TX
    Flg_Tx = 1;
  else
    Flg_Tx = 0;

  Fnc_TRdsp();                      // T/R Display

//////////////////////////
//  RX
//////////////////////////
  if(Flg_Tx == 0){                  // RX
    display_smeter(analogRead(A2)); // s-meter Display
    Fnc_Stp();
    si5351a_enable(0x00);
     
    val = analogRead(A0);
    delay(1);
    if(SW_RIT)                      // SW_RIT
      Fnc_Rit();                    // RIT Flag set

    if(((Vfo_Dat != Vfo_Datb) && (Flg_Rit == 0)) || (Flg_Disp == 1)){
      si5351aSetFrequency(Vfo_Dat + IF_FREQ);
      band_check();
      mode_disp();
      Fnc_Fdsp(Vfo_Dat);
      Vfo_Datb = Vfo_Dat;
      Flg_Disp = 0;
    }

    if(((Rit_Dat != Rit_Datb) && (Flg_Rit == 1)) || (Flg_Rdisp == 1)){
      si5351aSetFrequency(Vfo_Dat + IF_FREQ + Rit_Dat);
      rit_disp(Rit_Dat);
      Rit_Datb = Rit_Dat;
      Flg_Rdisp = 0;
    }

    si5351aSetFrequency2(Bfo_Dat);  // Bfo data out
  }

//////////////////////////
//  TX
//////////////////////////
  else{                           
    mode_disp();

    if(Flg_Over == 0){
      si5351a_enable(0x00);
      si5351aSetFrequency(Vfo_Dat + IF_FREQ);
    }
    else{                           // Frequency over
      si5351a_enable(0x07);
    }

    if(Flg_Rit == 1)
      Flg_Rdisp = 1;
    else
      Flg_Disp = 1;
  }

//////////////////////////
//  Save to EEPROM
//////////////////////////
  if(Flg_eepWT == 1){               // EEPROM auto Write
    if(Time_Passd+2000 < millis()){
      eep_wdata();
      Flg_eepWT = 0;
    }
  }
}

//----------  Function Rit  ---------

void Fnc_Rit(){
  unsigned int val;

  if(Flg_Rit == 0){
    Rit_Dat = 0;
    Flg_Rit = 1;
    lcd.setCursor(11,0);
    lcd.print("+0   ");
  }
  else{
    mode_disp();
    si5351aSetFrequency(Vfo_Dat + IF_FREQ);
    Flg_Rit = 0;
  }

  do{
    val = analogRead(A0);
    delay(1);
  }
  while(SW_RIT);
}

//----------  RIT Display  ---------------------------------------------

void rit_disp(int rit_data) {
  unsigned int ri,rit;

  lcd.setCursor(11,0);

  if (rit_data >= 0){
    lcd.print("+    ");
    lcd.setCursor(12,0);
    lcd.print(rit_data,DEC);
  }
  else{
    lcd.print("     ");
    lcd.setCursor(11,0);
    lcd.print(rit_data,DEC);
  }
}

//---------- Mode display ----------------------------------------------

void mode_disp() {
  lcd.setCursor(11,0);
  if((Flg_Tx == 1) && (Flg_Over == 1))
    lcd.print(" Over");             // frequency over
  else if (Flg_Mode == 0)
    lcd.print("   CW");             // CW
  else if(Flg_Mode == 1)
    lcd.print("  LSB");             // LSB
  else if(Flg_Mode == 2)
    lcd.print("  USB");             // USB
}

//----------  LCD Bar Display PROC.  ------------------------

void display_smeter(int strength){
// range is 0 to 1024 of adc
//meter run from position 3 to 14 (12 )
  int scale = (strength*15)/1024;
  lcd.setCursor(0, 1);
  lcd.print("S");
  int smeter=(scale*11)/15;
  if(smeter<10)
    lcd.print(smeter);
  else
    lcd.print("9");
  lcd.print(">");

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= 1000) {
    previousMillis = currentMillis;
    //clear all after a small interval
    lcd.setCursor(3, 1);
    lcd.print("            ");
    lcd.noCursor();
  }

  // write it and show for 500 milli sec
  for (int i = 3; i<scale; i++){
    lcd.setCursor(i, 1);
    lcd.write(byte(0));
    if(smeter>9)
      lcd.print("+");
  }
}


//----------  Encorder procedure(INT)  ----------------------

ISR(PCINT0_vect) {
  unsigned char result = r.process();

  if(Flg_Tx == 0){
    if(result) { 
      Enc_Velocity = millis() - Enc_Velocityb;

      if(result == DIR_CW){
        Lng_Wk = Vfo_Dat + Enc_Step;
        Int_Wk = Rit_Dat + Enc_Step;
      }
      else{
        Lng_Wk = Vfo_Dat - Enc_Step;
        Int_Wk = Rit_Dat - Enc_Step;
      }

      if(Flg_Rit == 1)
        Rit_Dat = Int_Wk;
      else{
        Vfo_Dat = Lng_Wk;
        Rit_Dat = 0;
      }

      Rit_Dat = constrain(Rit_Dat,LW_RIT,HI_RIT);  // RIT range check

      Flg_eepWT = 1;
      Time_Passd = Enc_Velocityb =millis();
    }
  }
}

//----------  Function Send/receive Display  ------

void Fnc_TRdsp(){
  lcd.setCursor(0,0);
  if(Flg_Tx == 1)
    lcd.print("T");
  else
    lcd.print("R");
}

//----------  Function Frequency Display  ---------

void Fnc_Fdsp(long f_disp){
  Fnc_Dot_Edit(Lcd_Dat,f_disp);
  lcd.setCursor(1,0);
  lcd.print("          ");
  lcd.setCursor(1,0);
  lcd.print(Lcd_Dat);
}

//----------  Function String Dot Edit  --------
 
char *Fnc_Dot_Edit(char *str,long n){
  int  i = 0;                       // Write the number
  char *p = str;
  unsigned long  u = abs(n);

  do{
    *p++ = "0123456789"[u % 10];
    u = u / 10;
    i++;
    if((0 != u) && (0 == (i % 3)))
      *p++ = '.';
    }
  while( 0 != u )
    ;
  if ( n < 0 )
     *p++ = '-';
   *p = '\0';
   Fnc_Revr( str );
   return str;
}

//----------  Function String Reverse  ---------------------------------

void Fnc_Revr(char *str){
  int i,n;
  char c;

  n=strlen(str);
  for(i = 0;i < n / 2;i++){
    c=str[i];
    str[i]=str[n - i - 1];
    str[n - i - 1]=c;
    }
}

//----------  Write EEPROM 4byte  --------------------------------------

void eep_write4(long value,int address){
  address += 3;
  for(int i = 0;i < 4;i++){
    byte toSave = value & 0xFF;
    if(EEPROM.read(address) != toSave){
      EEPROM.write(address,toSave);
      }
    value = value >> 8;
    address--;
  }
}

//----------  Read EEPROM 4byte  ---------------------------------------

long eep_read4(int address){
  long value = 0;

  for(int i = 0;i < 4;i++){
    value = value | EEPROM.read(address);
    if( i < 3){
      value = value << 8;
      address++;
    }
  }
  return value;
}

//----------  EEPROM Dat Read  -----------------------------------------

void eep_rdata(){
  Vfo_Dat = eep_read4(Eep_Freq);
  delay(10);
}

//----------  EEPROM Dat Write  ----------------------------------------

void eep_wdata(){
  eep_write4(Vfo_Dat,Eep_Freq);
  delay(10);
}
 
//----------  EEPROM Initialization ------------------------------------

void eep_init(){
  int i;

  for (i=0;i<64;i++)                // 0 clear(128byte)
    EEPROM.write(i, 0);

  eep_write4(FRQ_TBL[DEF_F],Eep_Freq);
  EEPROM.write(Eep_Init,Int_End);   // Init end set(73)
}

//----------  Function Encorder STEP  ----------------------------------

void Fnc_Stp(){
  if(Time_Passd+2000 < millis()){
    Enc_Step = 10;
  }

  else{
    if(Enc_Delay+6000 < millis()){
      if(Enc_Velocity < 100)        // chenge STEP?
        Enc_Step = Enc_Step * 10;
      if(Enc_Step > 10000)          // 10kHz over?
          Enc_Step = 10;            // STEP = 10Hz
      Enc_Delay = millis();
    }
  }

}

//----------  band chek  -----------------------------------------------

void band_check(){
  if((Vfo_Dat >= FRQ_TBL[LOW_F]) && (Vfo_Dat <= FRQ_TBL[MID_F])){
    Flg_Mode = 0;                   // CW(0=CW, 1=LSB, 2=USB) 
    Bfo_Dat = CW;
    Flg_Over = 0;
  }

  else if((Vfo_Dat >= FRQ_TBL[MID_F]) && (Vfo_Dat <= FRQ_TBL[HI_F])){
    if(Vfo_Dat >= 10000000){        // greate than 10MHz                     
      Flg_Mode = 2;                 // USB(0=CW, 1=LSB, 2=USB)
      Bfo_Dat = USB;
    }
    else{
      Flg_Mode = 1;                 // LSB(0=CW, 1=LSB, 2=USB)
      Bfo_Dat = LSB;
    }
    Flg_Over = 0;
  }

  else{
    Flg_Over = 1;
    if(Vfo_Dat >= 10000000){        // greate than 10MHz                     
      Flg_Mode = 2;                 // USB(0=CW, 1=LSB, 2=USB)
      Bfo_Dat = USB;
    }
    else{
      Flg_Mode = 1;                 // LSB(0=CW, 1=LSB, 2=USB)
      Bfo_Dat = LSB;
    }
  }
}

実装写真

Adafruit純正または中華製モジュール

秋月電子製モジュール

0 件のコメント: