オーディオタイマーで換気2
 
前に書いた記事
https://ameblo.jp/fc2miha/entry-12833772945.html


ソースが間違っていたので修正。
arduinoのmillis関数が約50日間でオーバーフローし、ゼロに戻ります。
の件で、対処のプログラムを入れておいたはずだけどー
それにまんまとハマったらしいので
治しました。
■ソース
 
これも間違っておりましたので
 修正版は→https://ameblo.jp/fc2miha/entry-12833773004.html

 

 

#include <stdio.h>
#include <time.h>

#include <LiquidCrystal.h>
//LCD        arduino
// 4(rs)     D12
// 5(rw)     D11
// 6(enable) D10 
// 11(db4)    D5
// 12(db5)    D4
// 13(db6)    D3
// 14(db7)    D2
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);

//
//D8をSSRへ接続
//

struct BUTTON
{
  int value;
  int flag;
  int input_pin;
};
struct BUTTON bt_select;
struct BUTTON bt_enter;
int lcd_back_light = 1;

void button_initialize()
{
  memset(&bt_select, NULL, sizeof(bt_select));
  bt_select.input_pin = 6;
  memset(&bt_enter, NULL, sizeof(bt_enter));
  bt_enter.input_pin = 7;
}

void tick_button(struct BUTTON *bt)
{
  int value = digitalRead(bt->input_pin);
  if(value!=bt->value){
    bt->value = value;
    bt->flag = 1;
//    if(value==HIGH){
//      digitalWrite(13, HIGH);
//      lcd_back_light = 1;
//    }
    delay(10);
  }
}

int get_button(int *status, struct BUTTON *bt)
{
  if(bt->flag == 1){
    *status = bt->value;
    bt->flag = 0;
    return 1;
  }
  else{
    return 0;
  }
}

void setup()
{
  Serial.begin(9600); 
  
  delay(620);

  Serial.print("AcPowerControler version0.2 ¥n");
  
  //
  button_initialize();
  alarm_initialise();

  //lcd back light
  digitalWrite(13, HIGH);

  //SSR
  pinMode(8, OUTPUT);
  digitalWrite(8, LOW);
  //Input
  pinMode(6, INPUT);  //enter button
  pinMode(7, INPUT);  //select button
  //back light
  pinMode(13, OUTPUT);  //LCD back light

  //LCD
  lcd.begin(2,16);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("AcPowerControler");
  delay(500);
  //
  setup_time_input();
}

void setup_time_input()
{
  int YYYY = input("input year?", 4, 2020, 2020, 2100);
  int MM = input("input month?", 2, 1, 1, 12);
  int DD = input("input day?", 2, 1, 1, 31);
  int hh = input("input hour?", 2, 0, 0, 23);
  int mm = input("input minits?", 2, 0, 0, 59);

  initDateTime(YYYY, MM, DD, hh, mm);

  lcd.clear();

}

int input(char *str, int keta, int def, int mMin, int nMax)
{
  lcd.clear();
  lcd.print(str);

  int  i = 0;
  char buf[17];
  char ch[2];
  //
  lcd.blink();
  //
  int value = def;

  int flag = 1;
  int status;
  while(1){
    tick_button(&bt_select);
    tick_button(&bt_enter);

    if(flag==1){
      //
      if(keta==4){
        sprintf(buf, "%04d", value);
      }
      if(keta==2){
        sprintf(buf, "%02d", value);
      }
      if(keta==1){
        sprintf(buf, "%01d", value);
      }
      lcd.setCursor(0, 1);
      lcd.print(buf);
      lcd.setCursor(keta-1, 1);
      flag = 0;
    }
    if(get_button(&status, &bt_enter)){
      if(status==HIGH){
        return value;
      }
    }
    if(get_button(&status, &bt_select)){
      if(status==HIGH){
        flag = 1;
        value++;
        if(value>nMax){
          value = mMin;
        }
      }
    }
  }
}

void lcd_print(int x, int y, char *str)
{
  lcd.setCursor(x, y);
  lcd.print(str);
}

struct DateTime {
  time_t base_time;
  unsigned long BaseMillis;
  unsigned long base_startup_keika_sec;
};
struct DateTime date_time;


void initDateTime(int YYYY, int MM, int DD, int hh, int mm)
{
    struct tm now;
    now.tm_year = YYYY-1900;     /* 2001年 */
    now.tm_mon  = MM-1;       /* 5月    */
    now.tm_mday = DD;      /* 20日   */
    now.tm_wday = 0;       /* 日曜日 */
    now.tm_hour = hh;      /* 14時   */
    now.tm_min  = mm;      /* 20分   */
    now.tm_sec  = 0;       /* 00秒   */
    now.tm_isdst= -1;      /* 夏時間無効 */
    
    date_time.base_time = mktime(&now);
    if (date_time.base_time == (time_t) - 1) { //error
      date_time.base_time = 0;
    }
    date_time.BaseMillis = millis();
    date_time.base_startup_keika_sec = date_time.BaseMillis / 1000;
}

struct HHMM{
  int HH;
  int MM;
  int YOUBI;
};
struct HHMM hhmm;

unsigned long getSec()
{
  //現在時刻累積秒を求める
  unsigned long now_startup_keika_sec = millis() / 1000;
  return date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
}

void getDateTime(struct HHMM *hhmm)
{
  //現在時刻累積秒を求める
  unsigned long now_startup_keika_sec = millis() / 1000;
  time_t now_time = date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
  
  //localtimeにする
  //now_time = now_time + 60 * 60 * 9;

  struct tm   *time_inf;
  time_inf = localtime(&now_time);

  hhmm->HH = time_inf->tm_hour;
  hhmm->MM = time_inf->tm_min;
  hhmm->YOUBI = time_inf->tm_wday;
}

void getDateTimeStr(char *buf1, char *buf2)
{
  //現在時刻累積秒を求める
  unsigned long now_startup_keika_sec = millis() / 1000;
  time_t now_time = date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
  
  //localtimeにする
  //now_time = now_time + 60 * 60 * 9;

  struct tm   *time_inf;
  time_inf = localtime(&now_time);

  int year = time_inf->tm_year + 1900;
  int month = time_inf->tm_mon + 1;
  int day = time_inf->tm_mday;
  int hour = time_inf->tm_hour;
  int minute = time_inf->tm_min;
  int second = time_inf->tm_sec;
  char *wday[] = {"SUN","MON","TUE","WED","TUE","FRI","SAT"};
  sprintf(buf1, "%04d/%02d/%02d %s", year, month, day, wday[time_inf->tm_wday]);
  sprintf(buf2, "%02d:%02d:%02d", hour, minute, second);
}

int debugCount=0;
void tick_time()
{
  int debugFlag = ((debugCount%100)==0);
  debugCount++;
  if(debugFlag){
    Serial.println("tick_time() start");
  }
  unsigned long now = millis();
  if(debugFlag){
    Serial.print("now=");
    Serial.println(now);
    Serial.print("date_time.BaseMillis=");
    Serial.println(date_time.BaseMillis);
  }
  if(now < date_time.BaseMillis){
    date_time.BaseMillis = now;
    //date_time.base_startup_keika_sec  = ((4294967295 - date_time.BaseMillis) + now)/1000;
    date_time.base_startup_keika_sec = date_time.BaseMillis / 1000;
    Serial.println("tick_time() point 1");
  }
  if(debugFlag){
    Serial.println("tick_time() end");
  }
}

struct ALARM
{
  int mode;   //  0=week, 1=alway on, 2=alway off, 3=after on, 4=after off
  int SUN;
  int MON;
  int TUE;
  int WED;
  int THU;
  int FRI;
  int SAT;
  int start_hh;
  int start_mm;
  int end_hh;
  int end_mm;
  int after_on_hh;
  int after_off_hh;
  unsigned long setSec;
};
struct ALARM alarm;

int flag = 0;
unsigned long nCount = 0;
void loop()
{
  nCount++;

  //lcd.noCursor();

  tick_time();
  tick_button(&bt_select);
  tick_button(&bt_enter);

  //LCD back light
  if(lcd_back_light==1){
    digitalWrite(13, HIGH);
  }
  else{
    digitalWrite(13, LOW);
  }
  
  //DEBUG
  //int kakeru = 60;    //DEBUG
  int kakeru = 3600;  //本番向け
  //
  unsigned long nowSec = getSec();
  //表示
  if(alarm.mode==0 || alarm.mode==1 || alarm.mode==2){  //week alway
    char date[20];
    char time[20];
    getDateTimeStr(date, time);
    lcd_print(0, 0, date);
    lcd_print(0, 1, time);
    if(alarm.mode==0){
      lcd_print(8, 1, " weekly  ");
    }
    if(alarm.mode==1){
      lcd_print(8, 1, " all ON  ");
    }
    if(alarm.mode==2){
      lcd_print(8, 1, " all OFF ");
    }
  }else
  if(alarm.mode==3 || alarm.mode==4){
    int sec;
    if(alarm.mode==3){
      sec = alarm.setSec+(alarm.after_on_hh * kakeru) - nowSec;
    }
    else{
      sec = alarm.setSec+(alarm.after_off_hh * kakeru) - nowSec;
    }
//    Serial.print("sec=");
//    Serial.println(sec);
    if(sec<0){
      sec=0;
    }
    int hh = sec / 3600;
    int mm = (sec - (hh * 3600))/60;
    int ss = sec % 60;
    char buf[17];
    sprintf(buf, "%02d:%02d:%02d", hh, mm, ss);
    lcd_print(0, 0, buf);
    if(alarm.mode==3){
      lcd_print(8, 1, " afterON ");
    }
    if(alarm.mode==4){
      lcd_print(8, 1, "afterOFF");
    }
  }
  

  lcd.setCursor(1, 0);

  //現在時刻取得
  struct HHMM hhmm;
  getDateTime(&hhmm);
  //日付跨ぎの補正
  int alarmStartHHMM = (alarm.start_hh * 100 + alarm.start_mm)  ;
  int alarmEndHHMM   = (alarm.end_hh * 100 + alarm.end_mm);
  int nowHHMM = (hhmm.HH * 100 + hhmm.MM);
  if(alarmStartHHMM>alarmEndHHMM){
    alarmEndHHMM = alarmEndHHMM + 2400;
  }
  if(alarmStartHHMM>nowHHMM){
    nowHHMM = nowHHMM + 2400;
  }
  //
  if(alarm.mode==0){  //week
    //
    if((hhmm.YOUBI==0 && alarm.SUN==1)
     ||(hhmm.YOUBI==1 && alarm.MON==1)
     ||(hhmm.YOUBI==2 && alarm.TUE==1)
     ||(hhmm.YOUBI==3 && alarm.WED==1)
     ||(hhmm.YOUBI==4 && alarm.THU==1)
     ||(hhmm.YOUBI==5 && alarm.FRI==1)
     ||(hhmm.YOUBI==6 && alarm.SAT==1)){
  /*    
   *   if( (alarm.start_hh * 100 + alarm.start_mm) <= (hhmm.HH * 100 + hhmm.MM) 
                                                  && (hhmm.HH * 100 + hhmm.MM) < (alarm.end_hh * 100 + alarm.end_mm)){
  */
      if( alarmStartHHMM <= nowHHMM && nowHHMM < alarmEndHHMM){
        digitalWrite(8, HIGH);
      }
      else{
        digitalWrite(8, LOW);
      }
    }
    else{
      digitalWrite(8, LOW);
    }
  }else
  if(alarm.mode==1){    //alway on
    digitalWrite(8, HIGH);
  }else
  if(alarm.mode==2){    //alway off
    digitalWrite(8, LOW);
  }else
  if(alarm.mode==3){    //after on
    if(alarm.setSec+(alarm.after_on_hh * kakeru) >= nowSec){
      digitalWrite(8, LOW);
    }
    else{
      digitalWrite(8, HIGH);
    }
  }else
  if(alarm.mode==4){    //after off
    if(alarm.setSec+(alarm.after_off_hh * kakeru) <= nowSec){
      digitalWrite(8, LOW);
    }
    else{
      digitalWrite(8, HIGH);
    }
  }
  
  
  //
  int status;
  if(get_button(&status, &bt_select)){
    if(status==HIGH){
      //alarm_input();
      if(lcd_back_light==0){
        lcd_back_light = 1;
      }
      else{
        lcd_back_light = 0;
      }
    }
  }
  if(get_button(&status, &bt_enter)){
    if(status==HIGH){
      alarm_input();
    }
  }
  delay(10);
}

void alarm_initialise()
{
  memset(&alarm, NULL, sizeof(struct ALARM));
  alarm.SUN = 1;
  alarm.MON = 1;
  alarm.TUE = 1;
  alarm.WED = 1;
  alarm.THU = 1;
  alarm.FRI = 1;
  alarm.SAT = 1;
  alarm.after_on_hh = 1;
  alarm.after_off_hh = 1;
}

void alarm_input()
{
  //lcd back light
  digitalWrite(13, HIGH);
  //
  alarm.mode = select_input5("mode :", "weekly    ", "always on ", "always off", "after on  ", "after off ", alarm.mode);
  if(alarm.mode==0){
    //weekly
    alarm.SUN = select_input("[SUN] : ", "  ", "ON", alarm.SUN);
    alarm.MON = select_input("[MON] : ", "  ", "ON", alarm.MON);
    alarm.TUE = select_input("[TUE] : ", "  ", "ON", alarm.TUE);
    alarm.WED = select_input("[WED] : ", "  ", "ON", alarm.WED);
    alarm.THU = select_input("[THU] : ", "  ", "ON", alarm.THU);
    alarm.FRI = select_input("[FRI] : ", "  ", "ON", alarm.FRI);
    alarm.SAT = select_input("[SAT] : ", "  ", "ON", alarm.SAT);
  
    alarm.start_hh = input("start hour",   2, alarm.start_hh, 0, 23);
    alarm.start_mm = input("start minits", 2, alarm.start_mm, 0, 59);
    alarm.end_hh   = input("end hour",     2, alarm.end_hh,   0, 23);
    alarm.end_mm   = input("end minits",   2, alarm.end_mm,   0, 59);
  }
  if(alarm.mode==3){
    //after on
    alarm.after_on_hh = input("after hour",   1, alarm.after_on_hh, 1, 9);
    alarm.setSec = getSec();
    Serial.print("alarm.setSec=");
    Serial.println(alarm.setSec);
  }
  if(alarm.mode==4){
    //after off
    alarm.after_off_hh = input("after hour",   1, alarm.after_off_hh, 1, 9);
    alarm.setSec = getSec();
  }

  lcd.clear();

  lcd_back_light = 1;
}

int select_input(char *str, char *select1, char *select2, int def)
{
  lcd.clear();
  lcd.print(str);

  int  value = def;
  char buf[17];
  char select[17];
  //
  lcd.blink();
  //
  strcpy(buf, "");
  strcpy(select, select1);

  int flag = 1;
  int status;
  while(1){
    tick_button(&bt_select);
    tick_button(&bt_enter);

    if(flag==1){
      if(value==0){
        sprintf(buf, "%s", select1);
      }
      else{
        sprintf(buf, "%s", select2);
      }
      lcd.setCursor(0, 1);
      lcd.print(buf);
      lcd.setCursor(strlen(buf)-1, 1);
      flag = 0;
    }
    if(get_button(&status, &bt_enter)){
      if(status==HIGH){
        return value;
      }
    }
    if(get_button(&status, &bt_select)){
      if(status==HIGH){
        flag = 1;
        if(value==0){
          value = 1;
        }
        else{
          value = 0;
        }
      }
    }
  }
  return 0;
}

int select_input5(char *str, char *select1, char *select2, char *select3, char *select4, char *select5, int def)
{
  lcd.clear();
  lcd.print(str);

  int  value = def;
  char buf[17];
  char select[17];
  //
  lcd.blink();
  //
  int flag = 1;
  int status;
  while(1){
    tick_button(&bt_select);
    tick_button(&bt_enter);

    if(flag==1){
      if(value==0){
        sprintf(buf, "%s", select1);
      }else
      if(value==1){
        sprintf(buf, "%s", select2);
      }else
      if(value==2){
        sprintf(buf, "%s", select3);
      }else
      if(value==3){
        sprintf(buf, "%s", select4);
      }
      else{
        sprintf(buf, "%s", select5);
      }
      lcd.setCursor(0, 1);
      lcd.print(buf);
      lcd.setCursor(strlen(buf)-1, 1);
      flag = 0;
    }
    if(get_button(&status, &bt_enter)){
      if(status==HIGH){
        return value;
      }
    }
    if(get_button(&status, &bt_select)){
      if(status==HIGH){
        flag = 1;
        if(value==0){
          value = 1;
        }else
        if(value==1){
          value = 2;
        }else
        if(value==2){
          value = 3;
        }else
        if(value==3){
          value = 4;
        }
        else{
          value = 0;
        }
      }
    }
  }
  return 0;
}

ツェナーダイオードで12Vから5Vを生成
 
3端子レギュレーターが使えない状況で5Vとか3.3Vを生成したい場合の参考記事です。
 
回路図
 
 
使用したのはかなり前に秋月電子で買った 型番NXP 1N4733A というものです。
 

スペックを調べると
ツェナー電圧5.1Vで電力損失1Wって書いてあるね。
入力電圧として今回は12Vでテストするけど何ボルトまで出来るんでしょうかね?
おじさんとしては100V交流をそのまま整流してこいつで5Vまで下げられると
色々めんどくさくなくて大変よろしいんだけどね。
ちなみに、3.3Vであれば
GDZJ3.3B とか
他の電圧であれば以下に秋月のURL張っておく。
 http://akizukidenshi.com/catalog/c/czd_dI/
でつくれば作れます。
 
しかし、ダイオードって本当に他のと見分けがつかない。見てもわかんない。
■欠点として3端子レギュレーターに比較して無駄な電力消費が大きいので潤沢に電気を使用できる場合の参考です。
 
■VTR
 

 
ツェナーダイオードで取り出した5Vの先にはarduino互換機5V、16Mhz版を接続してLチカを動かしております。
黒いテスターが出力電圧を計測
白いテスターが入力の電流を測定。
入力は12Vで70mAでありますので、0.84Wですね。
arduino互換機は通常5Vで10mAぐらいなので、0.05Wですね。
なんと17倍もの電力をツェナーダイオードが消費してくれております。
ですが、簡単な回路で目的の電圧を得た場合は大変有用と思いますのでここに掲載しておきます。

 

Gmailでメール送信SSLでPython
 
Pythonでメール送信したいんだけど。
最近はメール送信するにもいろいろとめんどくさいっすね。
昔はポート接続してテキストでsmtpとやり取りすればよかったんだけどね。
 
メールは現時点で一番に手軽にアカウントが作れるのが
googleなので、googleに対応しておけばOKかなと思いますので
googleでメール送信する場合の例です。
 
ちなみに、googleでもセキュリティーは最低限ありまして
googleにログインした状態で
 GOOGLEアカウントを管理
  →セキュリティー
  →下の方の安全性の低いアプリのアクセスの所のアクセスと有効にする
  →ボタンをON(右側へ)としておくこと
 

■ソース

 

# -*- coding: utf-8 -*-

import smtplib
from email.mime.text import MIMEText
from email.utils import formatdate

FROM_ADDRESS = 'zzz.abc.12345@gmail.com'
MY_PASSWORD = 'password'
TO_ADDRESS = 'address1@xxxx.jp,address2@xxxx.jp'
BCC = ''
SUBJECT = 'Gmailでメール送信SSL'
BODY = 'Gmailでメール送信SSLできるかな?'


def create_message(from_addr, to_addr, bcc_addrs, subject, body):
    msg = MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Bcc'] = bcc_addrs
    msg['Date'] = formatdate()
    return msg


def send(from_addr, to_addrs, msg):
    smtpobj = smtplib.SMTP_SSL('smtp.gmail.com', 465, timeout=10)
    smtpobj.login(FROM_ADDRESS, MY_PASSWORD)
    sendToList=to_addrs.split(',')
    #smtpobj.sendmail(from_addr, to_addrs, msg.as_string())
    smtpobj.sendmail(from_addr, sendToList, msg.as_string())
    smtpobj.close()


if __name__ == '__main__':

    to_addr = TO_ADDRESS
    subject = SUBJECT
    body = BODY

    msg = create_message(FROM_ADDRESS, to_addr, BCC, subject, body)
    send(FROM_ADDRESS, to_addr, msg)

 

 

ちなみに
TO_ADDRESSにカンマで区切って複数の送信先を登録する事が可能です。

 

WioLTEを間欠運転で電池長持ちさせる記事を進化させてみた。
 
2月の記事で
WioLTEを間欠運転する記事を書いた
→ https://ameblo.jp/fc2miha/entry-12833772944.html


これはリレーを使用してWioLTE側の電源を制御するものだったが、
これだと、リレー動作時の電流が40mAぐらいになっており、
この部分を少なくしたいという課題があった。
それで、ほかの方法を探していましたが
見つけてきました。
 
■G3VM-21AR

 
まー、リレーみたいなやつで
入力側の電流を少なくできそうなやつです。
arduinoから直接制御できるので中間に位置していたトランジスターが省略可能になります。
 
入力(マイコン側)が1.33Vで10mAで制御可能なようです。
回路では抵抗100Ωにしましたちゃんと動きましたね。抵抗値は330ΩぐらいでもOKと思われ。
 
出力側は20V、3Aまで行けるようです。
 
■回路
 

 
■実験結果
実際に組んで電流を測定すると動作時の電流で19mAぐらいです。
前のと比較して半分ぐらいですね。
んーなかなかよろしい。
 
 

 

Windows10のコマンドでWIFIに自動接続する方法
 
既存のPCでプロファイルをファイルにエクスポート
別なパソコンにファイルをコピーしてインポートする
 
■周辺のSSIDを表示
 
netsh wlan show network
 
■既知のプロファイルを表示
 
netsh wlan show profiles
 
■既知のプロファイルをファイルにエクスポート
 
netsh wlan export profile name="KURENAI6"
■プロファイルを作成 上記でエクスポートしたファイルを読み込ませる
 
netsh wlan add profile filename=wifi.xml
 
■パスワードを指定、自動接続しない。上記のaddとセット
 
netsh wlan set profileparameter name=KURENAI6 keymaterial=0080868086 connectionmode=manual
 
■SSIDに接続
 
netsh wlan connect name="KURENAI6"
 
■WIFIを切断
 
netsh wlan disconnect
 
■既知のプロファイルを削除
 
netsh wlan delete profile name="KURENAI6"
 

 

ARDUINOだけで温度と電源電圧を測定する
 
ARDUINOのA0~A5を使用しないで
電源電圧の測定とCPU温度を取得する方法があるようなのでテストして記録しておきたい。
 
■回路図
 
internalの基準電圧1.1Vを使用するので
AREFにはコンデンサ0.1μFを接続するのが味噌です。
(味噌って表現わかる?)
 
 

 

■プログラム

 

温度取得する時に- 329.0を減算していますが、

これは調整する必要があります。

もし、サーバーに送るのならサーバー側で調整値持った方が楽かもです。

 

void setup()
{
  delay(1000);
  Serial.begin(9600); 
}
 
void loop()
{
  float temp, vcc;
  temp = cpuTemp();  // CPU温度測定
  vcc  = cpuVcc();   // 電源電圧測定

  char str[30];

  sprintf(str, "temp=%d.%d, vcc=%d.%d", int(temp), int(temp*10)%10, int(vcc), int(vcc*10)%10);
  Serial.println(str);

  delay(500);
}
 
float cpuTemp()
{
  long sum=0;
  adcSetup(0xC8);                    // 基準電圧をinternal(1.1V)にして 温度センサーの電圧を読む
  for(int n=0; n < 1000; n++){
    sum = sum + adc();               // adcの値を読んで積分
  }
  return (sum * 1.1/1024)- 329.0;   // 個体差が多きいので-329.0は要調整
                                    // 温度を計算
}
 
float cpuVcc()
{
  long sum=0;
  adcSetup(0x4E);                    // 基準電圧をinternal(1.1V)にして Vccの電圧を読む
  for(int n=0; n < 10; n++){
    sum = sum + adc();               // adcの値を読んで積分
  }
  return (1.1 * 10240.0)/ sum;       // 電圧を計算
}
 
void adcSetup(byte data)
{
  //ADMUX                       
  
  // 入力電圧 選択 下位bit
  //  0bxxxx0000 A0
  //  0bxxxx0001 A1
  //  0bxxxx0010 A2
  //  0bxxxx0011 A3
  //  0bxxxx0100 A4
  //  0bxxxx0101 A5
  //  0bxxxx1000 Temp Sensor  CPU内蔵温度センサー  
  //  0bxxxx1110 1.1V Vref    internal基準電圧1.1V
  
  // 基準電圧 選択 上位2bit
  // 0b00xxxxxx Ext
  // 0b01xxxxxx AVcc
  // 0b10xxxxxx Reserve
  // 0b11xxxxxx Internal 1.1V
  
  //
  ADMUX = data;                      // 上記ビットで選択
  
  ADCSRA |= ( 1 << ADEN);            // ADC Enable
  
  ADCSRA |= 0x07;                    // AD変換クロック CK/128
  
  delay(20);                         // 少し待つ
}
 
unsigned int adc()
{
  unsigned int L, H;
  ADCSRA |= ( 1 << ADSC);            // AD変換
  while(ADCSRA & ( 1 << ADSC) );     // 完了待ち
  
  L = ADCL;                         // LSB
  H = ADCH;                         // MSB
  
  return L | (H << 8);              // 合成
}

 

djangoを覚えるぞ。

 

■環境作成

 

pip install django

 

■プロジェクトを作成

 

django-admin startproject プロジェクト名

 

■プロジェクトにアプリケーションを作成

 

python manage.py startapp アプリケーション名

 

■dhangoサーバーを起動

 

python manage.py runserver

 

■デフォルトでのURL

 

http://127.0.0.1:8000/


■データベース更新用ファイル作成

 

先にモデルを更新する必要がある

 

python manage.py makemigrations

 

■データベース更新

 

python manage.py migrate

 

■管理者の作成

 

python manage.py createsuperuser
 miha/mihaで作成

 

■管理画面にモデルを登録

 

admin.pyに行を追加
from .models import モデル名
admin.site.register(モデル名)

 

■管理ツールにログイン

 

http://127.0.0.1:8000/admin

 

■ログイン画面経由でのアクセス

 

from django.contrib.auth.decorators import login_required

 

#views関数の一行前に以下の一行を追加
@login_required(login_url='/admin/login/')
def index(request) :

 

■ログイン画面経由のユーザーを管理画面から追加するときは

 

Staff statusにチェックを入れないと駄目

 

■管理画面/ログイン画面を日本語化

 

LANGUAGE_CODE = 'ja'


TIME_ZONE = 'Asia/Tokyo'

 

■ログイン画面をプチカスタマイズ

 

urls.py

 

from django.contrib import admin

admin.site.site_title = 'タイトルタグ' 
admin.site.site_header = 'サンプルアプリケーション' 
admin.site.index_title = 'メニュー'


■ログアウト

 

settings.py

 

以下の行を追加


 LOGOUT_REDIRECT_URL = '/web/'

 

テンプレートに
  <a href="logout/">ログアウト</a>
を追加

 

urls.pyに
     from django.contrib.auth import views as auth_views

     path('logout/', auth_views.LogoutView.as_view(), name='logout'),
を追加
 

■接続するホストの許可

 

setting.py

 

ALLOWED_HOSTS = [*]

 

起動方法を以下に変更

python manage.py runserver 0.0.0.0:8000


 

 

■サンプルソース

 

https://drive.google.com/drive/folders/1Aq0G8dsy3jh3I9UpEZam3LhKBEIGaLif?usp=sharing


 

トラ技biosとかの話(D78F0730) USBシリアル-GPIO-標準ライブラリ
 
昨日はトラ技biosの開発環境を入れてみてLチカを動かすところまで
やってみました。

 
本の通りにやっているつもりでもなかなかムズイっす。
なんか同じにならないので
サンプルで入っていたledramのプロジェクトをそのままコピーしてソースだけ入れ替えるとうまく行ったので
そちらを神としてなんとか出来ている感じですね。
今回は実際にトラ技biosをいろいろ使ってみる事にします。
 
そこで足を生やしてテストします。
 

 
■USB-SERIAL読み書き

 

#pragma sfr

#include "trgbios.h"

void wait(unsigned short len)
{
    while(len--)
    {
        #asm
        NOP
        #endasm
    }
}

const char STR[] = "USB制御¥n";

void main() // メイン
{
    trg_usbopen();

    P6.1 = 1;

    trg_puts(STR);
    trg_puts("xキーで終了¥n");
    
    while(1)
    {
        if(trg_getrecvcount( )){
            char c;
            trg_puts("p¥n");
            c = trg_getc();
            trg_senddata(&c,1);
            if(c == 'x'){
                break;
            }
        }
        else{
            wait(1000);
        }
    }
    trg_puts("¥n");

    P6.1 = 0;
}


■GPIO-Read-Write

 

#pragma sfr

#include "trgbios.h"

void wait(unsigned short len)
{
    while(len--)
    {
        #asm
        NOP
        #endasm
    }
}

const char STR[] = "GPIO制御¥n";

void main() // メイン
{
    unsigned short flag = 0;

    trg_usbopen();

    PM6.0 = 1;    //6.0を入力にする
    
    PM6.1 = 0;    //6.1を出力にする

    trg_puts(STR);
    trg_puts("xキーで終了¥n");
    
    while(1)
    {
        if(P6.0==0){
            if(flag==1){
                P6.1 = 0;    //6.1をLED消灯
                trg_puts("0¥n");
                flag = 0;
            }
        }
        else{
            if(flag==0){
                P6.1 = 1;    //6.1をLED点灯
                trg_puts("1¥n");
                flag = 1;
            }
        }

        if(trg_getrecvcount( )){
            char c;
            c = trg_getc();
            trg_senddata(&c,1);
            if(c == 'x'){
                break;
            }
        }
        else{
            wait(1000);
        }
    }
    trg_puts("¥n");

    P6.1 = 0;
}

 

■FLASH(ROM)からユーザプログラムを起動

 

ProjectWindowのソース・ファイルの所
 RamStart.asmを削除
 RomStart.asmを追加


ツール→リンカオプション→その他
ディレクティブファイル 参照してROMAPR.drを指定


その他はRAMで動かす時と同じで

ビルドする。それでFLASH(ROM)で動かせるhexファイルが作成される。

 

トラ技biosでflashコマンド投入
  2000-23FF の範囲を含むHEXファイルを入力してください
  Drop a HEX file.
   と出るので

上記でビルドしたhexファイルをドラッグしてteratermにドロップする

complate
と出て続けて
 2400-27FF の範囲を含むHEXファイルを入力してください
と出るので後は繰り返し。


プログラムの大きさによって繰り返す回数が異なるが
必要な回数が終わったらctrl+cで終了

 

hexファイルは分割する必要はなく、トラ技biosで適切に書き込んでくれるようです。

 

終わったら2000番からプログラムが書き込まれているので


トラ技biosの
 jmp 2000
  コマンドで実行開始できる。

 

電源投入と同時にflashに書き込まれたプログラムを動かしたい場合は
J2をショートさせる。

 

■標準ライブラリを使用する RAM版

#pragma sfr

#include <stdio.h>

#include "trgbios.h"

void wait(unsigned short len)
{
    while(len--)
    {
        #asm
        NOP
        #endasm
    }
}

const char STR[] = "標準ライブラリ¥n";

volatile int errno;

void main() // メイン
{
    unsigned short flag = 0;
    char str[30];
    int i=0;
    
    errno = 0;

    trg_usbopen();
    
    wait(10);

    trg_puts(STR);
    while(1){
        sprintf(str, "i=%d¥n", i);
        trg_puts(str);
        i++;
    }
}

 

□設定
 
 RAMAPP.drに以下の行が有る事を確認
 
  MERGE @@LCODE : = IXRAM
 
 ツール→コンパイルオプション→スタートアップ・ルーチン
 標準のライブラリを使用するのチェックを入れる
 浮動小数点対応の・・・のチェックを入れる
 
 浮動小数点の対応をする場合はグローバル変数変数で
  volatile int errno;
 mainで
  errno = 0;
   として明示的に初期化する
   
■標準ライブラリを使用する ROM版

 

プログラムはRAM版と同じ

 

設定

 ProjectWindowのソース・ファイルの所
  RamStart.asmを削除
  RomStart.asmを追加
 ツール→リンカオプション→その他
 ディレクティブファイル 参照してROMAPR.drを指定

 ROMAPP.drに以下の行が有る事を確認
 
  MERGE @@LCODE : = UROM

 その他はRAMで動かす時と同じで
 上の「FLASH(ROM)からユーザプログラムを起動」の所のflashコマンドの所からと同じ

って事で。
今日はこのぐらいで許してやるか。

トラ技biosとかの話(D78F0730)
 
先週の記事

では、トラ技基盤のドライバーをインストールして
トラ技biosが起動するところまで確認しました。
しかし、
コンパイラとかライブラリが無いとそれ以上進めなかったので
これを購入。
 

『USBマイコンでパソコンI/O![78K0基板付き]―Windowsから自在にコントロール (マイコン活用シリーズ)』
中古なので2000円ぐらい。
300円で買った基盤を動かすために2000円の出費。
ま、趣味なんで。良いんす。
 
ちなみに、並べて写真を撮ると、そっくりですね。
裏面にNEC、マルツパーツ、トラ技、村田製作所のロゴと
78K0 USBマイコン基板 TR0808U
 と印刷されています。
 

それぞれ左が秋月で右が本のおまけ

 

で、わかったことを書いていきますね。

 

■スペック

 

CPU:μPD78F0730
   8ビット
クロック:16Mhz
メモリ:
 ROM:16Kバイト
 RAM:3Kバイト(高速1Kバイト)
タイマー:5チャンネル

インターフェース
 USB2.0
 GPIO 19本(本基板では15本使用可能)
   UART、PWM
 AD/DAコンバーターは無い

 

■ピン配置

 

CN1
 15:GND
 16:V50
 17:P12
 18:P11
 19:P10
 20:GND
 21:GND
 22:FLMD0
 23:~RESET
 24:P30
 25:P01
 26:P00
 27:P120
 28:GND

CN2
 14:GND
 13:V50
 12:P31
 11:P32
 10:P61/LED
  9:P60
  8:VIO
  7:P33
  6:P17
  5:P16
  4:P15
  3:P14
  2:P13
  1:GND


■トラ技基盤のメモリマップ

 

FFFF
 特殊機能レジスタ
FEFFF
 内部高速RAM(1024バイト)
FB00
 USB用領域
 ---------
 使用不可領域
F7FF
 内部拡張RAM(2048バイト)
 ユーザプログラム領域
F000
EFFF
 使用不可
4000
3FFF
 フラッシュメモリ(16Kバイト)
 内蔵ROM
0000

 

■トラ技bios起動時のメモリマップ0000~3FFF(フラッシュメモリ)の詳細

 

3FFF
 ユーザプログラム領域
2000
1FFF
 トラ技bios格納領域
1800
 ブートコード格納領域
 ベクタテーブル等
0000

 

■トラ技biosのコマンド

 

load:HEXファイルをRAM上にロードする

 

 ex...
  load
 パラメータ
  無し
 使い方
  teratermで接続してloadコマンドを入力すると
  Drop a HEX file.
  と出るので
  hexファイルをドラッグしてteratermにドロップ
  バイナリを指定してOK
  complete
  と出れば成功

 

flash:HEXファイルをフラッシュメモリにロードする

 

 ex...
  flash
 パラメータ
  無し
 使い方
  loadと同じようで違う。
  プログラムは1Kバイト単位でしか転送できないので
  コマンドの指示に従って最大8回転送する必要がある
  teratermで接続してflashコマンドを入力すると
  2000-23FF の範囲を含むHEXファイルを入力してください
  Drop a HEX file.
  と出るので、
  hexファイルをドラッグしてteratermにドロップ
  complate
  と出て続けて
  2400-27FF の範囲を含むHEXファイルを入力してください
  と出るので後は繰り返し。
  プログラムの大きさによって繰り返す回数が異なるが
  必要な回数が終わったらctrl+cで終了

  hexファイルは分割する必要はなく、トラ技biosがわで適切に書き込んでくれるようです。


wr:任意の番地に任意のデータを書き込む

 

 ex...
  wr f000 10
 パラメータ
  メモリ番地 値

dump:任意の番地のメモリ内容をdumpする

 

 ex...
  dump 0 2000
 パラメータ
  開始番地 バイト数

 

jmp:任意の番地にジャンプする

 

 ex...
  jmp F000
 パラメータ
  プログラムを実行する番地

 

■開発環境のセットアップ

 

アセンブラと統合開発環境をインストール


ra78k0_w401_j.exe


上記を起動してデフォルトのままインストール
(クリックして互換性のトラブルシューティングで
 WindowsXP(ServicePack3)でインストールするとよろしい)

 

C言語コンパイラをインストール


cc78k0_w400_j.exe
上記を起動してデフォルトのままインストール
(ここも右クリックして互換性のトラブルシューティングで
 WindowsXP(ServicePack3)でインストールするとよろしい)

 

デバイスファイルをインストール


df780731_v110.exe


上記exeを実行すると解凍先を聞かれるのでc:¥CQを指定して解凍
スタートメニュー→NEC Electronics Tools→デバイスファイルインストーラーを起動
インストールをクリックして


C:¥CQ¥df780731_v1.10¥NECSETUP.INI
 を指定して次へ→同意


 あとはデフォルトのまま進む
 レジストリの所にμPD78F0730とμPD78F0731が表示されるはず。
 終了しておく

 

■開発環境の使い方

 

起動してワークスペースを作る

 

 スタートメニュー→NEC Electronics Tools→PM+V6.30
起動したら
ファイル→ワークスペースの新規作成
ワークスペース・ファイル名とフォルダ位置を入力
次へ

使用ツールの指定の画面になる

・・・

ここまでやって
買った本の通りにならない。困った。投資が回収できない。回収するのは心の栄養ね。

・・・・・試行錯誤・・・・・・

とりあえず、やった事。

/*たぶんここは関係ない。
 * RENESASさんからe2_studioをダウンロードしてインストール
 * https://www.renesas.com/jp/ja/software/D4001299.html
 * なんかすごく時間がかかるので
 * 途中で飽きて
 *たぶんここまでは関係ない。
 */

元の本に戻って
cc78k0_w400_j.exeを再インストールした。
途中でWindowsが気を使ってくれて、互換性が何とかの画面になって
正常にインストールできなかった方のボタンをクリックしたところ
なんか、うまく行ったっぽい

使用ツールの指定の画面になる
CC78K0の所をW4.00 RA78K0の所をW4.01にしてOK
st78k9p.dllが見つからないといわれる。
OKを押すと今度は
ra78k0p.dllが無いといわれる。
以下同様に3個のdllが無いといわれ全部で5個のdllが無いらしい。

ra78k0_w401_j.exeの方も互換モードでインストールする必要があるっぽいですね。

ra78k0_w401_j.exeを右クリックして互換性のトラブルシューティングとやって、
WindowsXP(ServicePack3)でインストールしたら、

PM+を起動するショートカットをWindowsXP(ServicePack3)互換設定にする

上記の操作で次に進めた。もうDLLが無いとは言わせないよ。

あとはデフォルトのままで次へをクリック。最後に完了をクリックする。

 

ワークスペースにプログラムを追加

 

 作成したワークスペースを閉じている場合は
ファイル→ワークスペースを開く
として開く。
ファイル→新規作成一つサブウインドウが開くのでそこでプログラムを入力


void main()
{
}



ファイル→名前を付けて保存
適当にファイル名つけて今はsample1.cとした。
保存。


ワークスペースに最初から出ているProjectWindowでソースファイルの所を
右クリック→ソースファイルの追加
さっきのファイル(sample1.c)を選択して開く

ソースファイルの中に追加される。

コンパイル

 ビルドのボタンを押すまたはF7でもよいらしい
じゃ、F7で。


なんかうまく行ったっぽい。


こんな感じで表示された。


"C:¥Program Files (x86)¥NEC Electronics Tools¥CC78K0¥W4.00¥bin¥cc78k0.exe" -cF0730 -y"C:¥Program Files¥NEC Electronics Tools¥DEV" -_msgoff sample1.c
Compilation complete,    0 error(s) and    0 warning(s) found.
"C:¥Program Files (x86)¥NEC Electronics Tools¥RA78K0¥W4.01¥bin¥lk78k0.exe" -y"C:¥Program Files¥NEC Electronics Tools¥DEV" -_msgoff -oa.lmf "..¥..¥..¥..¥Program Files (x86)¥NEC Electronics Tools¥CC78K0¥W4.00¥lib78k0¥s0l.rel"  -pa.map -bcl0.lib -bcl0f.lib -i"C:¥Program Files (x86)¥NEC Electronics Tools¥CC78K0¥W4.00¥lib78k0" -s  sample1.rel 
Link complete,     0 error(s) and     0 warning(s) found.
"C:¥Program Files (x86)¥NEC Electronics Tools¥RA78K0¥W4.01¥bin¥oc78k0.exe" -y"C:¥Program Files¥NEC Electronics Tools¥DEV" -_msgoff  a.lmf
Object Conversion Complete,      0 error(s) and     0 warning(s) found.
Build Total error(s) : 0  Total warning(s) : 0 

 

□トラ技bios用になにかする

 

なになに、本によると
CDの中のtr0808.exeを解凍してcommonの中にあるファイルを5個コピーして
プロジェクトフォルダーにコピー。
RAMAPP.dr、RamStart.asm、ROMAPP.dr、RomStart.asm、trgbios.h
コピーしたファイルの
RAMAPP.dr、ROMAPP.drをPM+で開いて
MERGE @@LCODE  : =
の行をコメントアウトする
#MERGE @@LCODE  : = xxxx
xxxxはそれぞれ異なる

 

そしてさらに、
サンプルプログラムを以下に書き換える
#pragma sfr
void main()
{
  PM6.1 = 0;
  while(1){
    short i;
    for(i=0;i<30000;i++){
      
    }
    P6.1 ^= 0x01;
  }
}

 

コンパイルオプション変更


ツール→コンパイラオプションの設定
機能拡張→C++コメントの使用を許可するにチェックを付ける
スタートアップルーチン→標準のスタートアップルーチンを使用するのチェックを外す
標準のライブラリを使用するのチェックを外す

ソースファイルに先ほどコピーしたRamStart.asmをプロジェクトに追加
方法は
ProjectWindowでソースファイルの所を
右クリック→ソースファイルの追加

プロジェクト関連ファイルにRamStart.drを追加


方法は
ProjectWindowでプロジェクト関連ファイルの所を
右クリック→プロジェクト関連ファイルの追加

リンクの設定
ツール→リンカオプションの設定
出力ファイル名をsample1.lmf

ということで、なんか色々設定をいじった。


ビルド

で、


load 
ドラッグアンドップ


jmp f000

動かん。ピクリともしない。

 

 

・・・・・試行錯誤・・・・・・

 

・・・

 

 

・・

 

 

 

 


・・

ん、3時間経過

なんとか動くように調整できた。

 

■まとめ。

 

Windows10の場合

互換モードでインストールしないとうまく行かないっす。

 

□統合開発環境のインストール

 

ra78k0_w401_j.exe

インストーラーの実行は右クリックして互換性のトラブルシューティングでWindowsXP(ServicePack3)でインストールする。

上記を起動してデフォルトのままインストール

 

 

□C言語コンパイラをインストール


cc78k0_w400_j.exe

インストーラーの実行は右クリックして互換性のトラブルシューティングでWindowsXP(ServicePack3)でインストールする。

上記を起動してデフォルトのままインストール
 

□起動用アイコンもWindowsXP(ServicePack3)の互換モードに設定する。

 上記2つのツールの起動アイコンは互換モードの設定をする。

 

□デバイスファイルをインストール


df780731_v110.exe


上記exeを実行すると解凍先を聞かれるのでc:¥CQを指定して解凍
スタートメニュー→NEC Electronics Tools→デバイスファイルインストーラーを起動
インストールをクリックして


C:¥CQ¥df780731_v1.10¥NECSETUP.INI
 を指定して次へ→同意


 あとはデフォルトのまま進む
 レジストリの所にμPD78F0730とμPD78F0731が表示されるはず。
 終了しておく

 

□ファイルを取りそろえる。

 

適当な場所でプロジェクト用にフォルダを作成

 

□プログラム本体をプロジェクト用にフォルダに作成


[sample1.c]


#pragma sfr
void main()
{
  PM6.1 = 0;
  while(1){
    short i;
    for(i=0;i<30000;i++){
      
    }
    P6.1 ^= 0x01;
  }
}

 

□以下の4ファイルをプロジェクトファイル内に作成
    
    [ramstart.asm]
            NAME    @ramstart

            EXTRN   _main

        TSTA CSEG    AT      0F000H

        trgstart:
            BR      _main

            END


    [RAMAPP.dr]
        memory ROM     : ( 00000H, 01800H )
        memory BROM    : ( 01800H, 00800H )
        memory UROM    : ( 02000H, 02000H )
        memory IXRAM   : ( 0F000H, 00800H )
        memory RAM     : ( 0FB00H, 00500H )

        #プログラム中で明示的に定義されていないセクションの配置アドレスを指定する
        MERGE @@CODE : = IXRAM
        MERGE @@CNST : = IXRAM
        MERGE @@DATA : = IXRAM
    

    [ROMAPP.dr]
        memory ROM     : ( 00000H, 01800H )
        memory BROM    : ( 01800H, 00800H )
        memory UROM    : ( 02000H, 02000H )
        memory IXRAM   : ( 0F000H, 00800H )
        memory RAM     : ( 0FB00H, 00500H )

        #プログラム中で明示的に定義されていないセクションの配置アドレスを指定する
        MERGE @@CODE : = UROM
        MERGE @@CNST : = UROM
        MERGE @@DATA : = IXRAM
        
    [romstart.asm]
            NAME    @romstart

            EXTRN   _main

        TSTA CSEG    AT      02000H

        trgstart:
            BR      _main

            END


□トラ技biosのヘッダーをコピーして作成    
   toragi.h (これはCDからコピーする)

 

□起動してワークスペースを作る

 

 スタートメニュー→NEC Electronics Tools→PM+V6.30


起動したら


ファイル→ワークスペースの新規作成
ワークスペース・ファイル名とフォルダ位置を入力
次へ

□使用ツールの指定の画面になる


詳細設定をクリック
CC78K0の所をW4.00 にチェックを付ける
RA78K0の所をW4.01 にチェックを付ける
でOK
次へをクリック

□ソースファイルの設定になる


追加をクリックして
RamStart.asm
samle1.c
を指定
開くをクリック
次へ

残りはデフォルトで次へで進めて
完了をクリック

 

□設定を行う

 

ツール→コンパイラオプションの設定→機能拡張


C++コメントの使用を許可する
スタートアップルーチン→標準のスタートアップルーチンを使用するのチェックを外す
標準ライブラリを使用するのチェックを外す

出力→
アセンブラ・ソース・モジュール・ファイル出力のチェックを入れる
Cソース付き(インクルード非展開)にする
OK

 

ツール→リンカオプション→その他
ディレクティブファイル 参照してRAMAPR.drを指定

 

ツール→オブジェクトコンバーターオプションの設定→出力1

オブジェクト充てんのチェックを外す

OK

で、ビルド。

プロジェクトフォルダにhexファイルが出来る

loadコマンドで送り込んで
jmp f0000  実行。


おおーLEDぴかぴかしています。成功ですね。

μAを正確に測定できるテスターのアイデアが出たので実験してみた。
 
最近非常に微弱な電流を測定する機会が多くて困っている。
 
電流って測定するのには最終的には電圧を測定するのは理屈上わかっている。
でも、電流が少なくなればそれだけ回路に挟む抵抗を大きくする必要が出てきて
それによって、誤差や対象の回路に影響に与えて動作しなくなるんですよね。
 
それがいやなので
挟む抵抗を出来るだけ少なくして、その抵抗に発生した電圧を測ればいいんだけど、
事はそんなに簡単ではなくて
マイクロアンペアオーダーだと100Ωぐらいの抵抗は挟む必要が出てくる。
 
夢は0.1Ωぐらいまで落としたいって思っております。
本当は既に売っているのが有るのかなーなんて思ったりもしますが、
それはそれ。面白いからやるのであり、
世の中に無駄なことは無いと誰かが言っていた。
意味を取り違えているかもしれないけど、
失敗は成功の基という言葉もあるし。
 
ま、実験って事で。
 
なんかね、上で書いた
『回路に挟む抵抗を少なくした場合に』
『少ない電圧を測定するために』
つまり
『オペアンプで増幅すればいいんじゃねー?』
という夢を見たんです。
なんか4時ぐらいに起きちゃってこれって神の啓示かもとか思いこんで実験開始。
手持ちのオペアンプLM358Nで実験開示
で、とりあえず
1μAが目標なんですけど
とりあえず、テスターで測れるレベルの値から開始。
分圧後の電圧が0.5mA

グラフは後でね。
うーむ

・・
・・・
何度も測定して抵抗値も変えながら
やっておりましたが
どうやら、
超低電圧ではLM358Nが反応できないっぽい。
で、結果測定グラフ。こちら。
 
■グラフ
 

 
 
■結論
 
グラフの見方
 左のは測定値。
 上のは入力電圧に対する出力電圧の倍率。
  倍率は回路の抵抗2本で倍率が決定されます。
  計算式は
   倍率=(R2/R1)+1
   すげー計算値とぴったりですね。(ピッタリは50mV以上の所)
 下のは縦軸が電圧(単位はミリボルト)
結局は50mVから倍率として正確な数字が出るけどそれ以下では駄目である。
出力の上限が3.88Vでそれ以上は無理なので、それなりに設計する。
という事が判った。
ここから次の展開を考える。
 
 
■グラフを測定した回路はこちら。