果報は寝て待て: 2023

2023年7月5日水曜日

7segカウンターで遊ぶ

 アマゾンで1万円ちょいで中華製の大型LEDを買った。


 

 目的は、工場のあるライン内の、ある工程で払い出されたNG品の数をメイン通路の誰でも見やすい位置に表示することです。

要求は、大きい数字で表示できる。

一台ずつカウントアップできる。

リセットで0にできる。

最後に、責任者が確認したら、OK品だった場合のカウントダウンできることです。

しかし説明書にはカウントダウンできるとはどこにも書いてません。そこでまず1台買って改造できるか調べます。

製品の裏蓋を外した状態です。


PIC12F675が中央にあり、7SEGLED専用ドライバーのTM1620が右端に見えます。

ピンの接続は、

1-VDD, 

2-デジタルI/Oでカウントアップ、

3-デジタルI/Oでカウントを0にセット

4-未使用

5-データ送信

6-クロック送信

7-ストローブ信号送信

8-Vss

そう。4番ピンが余ってる。なんとかしてみよう。

ロジアナで[24]を表示している時の信号です。上から STB,CLOCK,DATAです。


データシートや他のかたのコードを参考にして、同じ数字のときに同じ信号になるようにコードを書きます。

 結論から言うと、データシートどおりでは無いところが2箇所ありました。

1 1桁目の数字はGRID1に表示され、2桁目の数字はGRID6に表示されます。

  3桁目以降の位置は不明。

2 SEG1=a,

  SEG2=b ・・・ SEG7=g のところ

     SEG1=d   ,SEG2=常時0    , SEG3=g     ,SEG4=a     ,SEG5=b    ,SEG6=c    SEG7=f  

        SEG8=c となってます。

これを加味したのが以下のコードです。マイコンはpic12f1822。そのままポン付けできそうなので…


/*
 * File:   main.c
 * Author: Administrator
 *
 * Created on April 11, 2023, 10:48 PM
 */
// PIC12F1822 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#define _XTAL_FREQ 4000000

#define STB RA0
#define CLK RA1
#define DIO RA2
#define count_up RA5
#define reset RA4
#define count_down RA3

const char NUM[11] = {
    0b11111001,    //0
    0b10010000,    //1
    0b10101101,    //2
    0b10011101,    //3
    0b11010100,    //4
    0b01011101,    //5
    0b01111101,    //6
    0b10011000,    //7
    0b11111101,    //8
    0b11011101,    //9
    0b00000000     //void
};

void Init();
void TM_Send(char data);
void NumToArr( int n, char* arr);
void TM_ShowNum( int n){   // 0<= n <= 999999
    char arr[6];
    NumToArr(n , arr);
    
    STB = 0;
    TM_Send(0b00000011);    //6X8
    STB = 1;
    __delay_us(9);
    STB = 0;
    TM_Send(0b01000000);    //Address auto increment
    STB = 1;
    __delay_us(9);
    STB = 0;
    TM_Send(0b11000000);    //Address SET
    
    TM_Send(NUM[arr[5]]);      //Charactor Data 1
    for(char i=0; i<9; i++){
        TM_Send(0);                //Dummy
    }
    TM_Send(NUM[arr[4]]);      //Charactor Data 10
    TM_Send(0);                //Dummy
    STB = 1;
    __delay_us(9);
    STB = 0;
    TM_Send(0x8f);                 //Duty 14 /16(max)
    DIO = 0;
    STB = 1;
}

void TM_Init(){
    STB = 1;
    CLK = 1;
    
    STB = 0;
    TM_Send(0x02);                 //6x8
    STB = 1;
    __delay_us(6);
    STB = 0;
    TM_Send(0x40);                 //Address auto increment
    STB = 1;
    __delay_us(6);
    TM_ShowNum(0);
    __delay_us(6);
    STB = 0;
    TM_Send(0x8f);                 //Duty 14 /16(max)
    STB = 1;
}

void main(void) {
    Init();
    TM_Init();
    OSCCON = 0b01110000;
    WPUA = 0xFF;
    OPTION_REGbits.nWPUEN = 0;
    int count = 0;
    int up_flug = 0;
    int down_flug = 0;
    
    while(1){
        TM_ShowNum(count);
        //count ++;
        __delay_ms(10);  // 50
        if (count_up == 0){
            if(up_flug == 0){
                count ++ ;
                up_flug = 1;
            }
        }
        else{
            if(up_flug == 1){
                 up_flug = 0;
            }
        }
        
        if (count_down == 0){
             if(down_flug == 0 & count > 0 ){
                  count -- ;
                  down_flug = 1;
            }
        }
        else{
            if(down_flug == 1){
                 down_flug = 0;
            }
        }
        if (reset == 0){
            count = 0;
        }
    }
}

void Init(){
    ANSELA = 0;
    TRISA = 0b00111000;
    
    
}
void TM_Send(char data){
    for(char i=0; i<8; i++){
        CLK = 0;
        DIO = (data) & 0x1;
        data = data>>1;
        __delay_us(1);
        CLK = 1;
    }
    __delay_us(1);
    DIO = 0;
}
void NumToArr(int n, char* arr){
    for(char i=0; i<6; i++){             //sprit n into arr
        arr[5-i] = n % 10;
        n /= 10;
    }
    for(char i=0; i<5; i++){           //delete initial zero
        if(arr[i] == 0 )
            arr[i]  = 10;              //void
        else break;
    }
}

計算は6桁まで  (本当は32767まで)できるのですが、2桁しか表示されません。

そのうち上限を99にするようコードを書き換えるでしょう。  

4番ピンは期待通りカウントダウンしてくれました。 





 


2023年5月31日水曜日

7セグLEDのエンコーダー?デコーダー?

 数字の信号を7セグのLEDに表示させる記事は山のようにありますが、7セグLEDから数字を取り出すのは見かけません。

 職場で使う責任者呼び出しベル(ファミレスのあれです)を活用してるのですが、最近どこのラインから、どのような用件で呼ばれたのかも記録に残すようにとの指示がありました。面倒なことは機械にやらせようと思い、作ってみました。

使ってるのはこれです。呼び出しに特化していて無駄な機能やI/Oが一切ありません。その分安いです。

やることは、このス◯ジオの左側の2桁の7セグ表示信号から数字を取り出してARUDUINO UNOでシリアル変換してパソコン上のエクセルかメモ帳に保存する。ということです。



 ス◯ジオのカバーを外すとLEDの乗った基盤が出てきます。PICマイコンからの信号でトランジスタをオンオフしています。14個のうち左の6個が6桁のドライブ、右の8個が7がセグ+DPのドライブ、信号はトランジスタのベースの足部分に細い線をはんだ付けして取り出します。7セグのA〜Gのうち、CとDの信号はなくても数字に変換できるので今回ははんだ付けしません。


 上の写真ですが、その信号をトランジスタのゲートで受けてAruduino unoのデジタルi/oピンに入力します。

uno のコードです。

 int a_7seg = 3; // 7セグ表示ledのA
int b_7seg = 4; // 7セグ表示ledのB
int e_7seg = 5; // 7セグ表示ledのE
int f_7seg = 6; // 7セグ表示ledのF
int g_7seg = 7; // 7セグ表示ledのG
int tenline_7seg = 10; // 7セグ表示ledの2桁
int dp_7seg = 11; // 7セグ表示ledのDP
int oneline_7seg = 12; // 7セグ表示ledの1桁
int led1  = 13;
int number  = 0;
int number_ten = 0;

void setup(){
    pinMode( a_7seg, INPUT_PULLUP );
    pinMode( b_7seg, INPUT_PULLUP );
    pinMode( e_7seg, INPUT_PULLUP );
    pinMode( f_7seg, INPUT_PULLUP );
    pinMode( g_7seg, INPUT_PULLUP );
    pinMode( tenline_7seg, INPUT_PULLUP );
    pinMode( dp_7seg, INPUT_PULLUP );
    pinMode( oneline_7seg, INPUT_PULLUP );
    
    Serial.begin( 9600 );
    pinMode( 13, OUTPUT );
}

void loop(){
    //  tenline_7segがLOWならば0.3ms後に2桁めの数字として取り込む
  if(digitalRead(tenline_7seg) == LOW ){
      delayMicroseconds(50);
      if (digitalRead(dp_7seg) == HIGH){
        if(digitalRead(e_7seg) == LOW){
          // 8,6,2,0
          if(digitalRead(f_7seg) == LOW){
            // 8,6,0
            if(digitalRead(b_7seg) == LOW){
              // 8,0
              if(digitalRead(g_7seg) == LOW){
                number_ten = 8;
              }else{
                number_ten = 0;
              }
            }else{
              number_ten = 6;
            }
          }else{
            number_ten = 2;
          }
        }else{
          // 9,7,5,4,3,1
          if(digitalRead(f_7seg) == LOW){
            // 9,5,4
            if(digitalRead(a_7seg) == LOW){
              // 9,5
              if(digitalRead(b_7seg) == LOW){
                number_ten = 9;
              }else{
                number_ten = 5;
              }
            }else{
              number_ten = 4;
            }
          }else{
            // 7,3,1
            if(digitalRead(a_7seg) == LOW){
              // 7,3
              if(digitalRead(g_7seg) == LOW){
                number_ten = 3;
              }else{
                number_ten = 7;
              }
            }else{
              if(digitalRead(b_7seg) == LOW){
                number_ten = 1;
              }else{
                number_ten = 0;
              }
            }
          }
        }
      }

 //  1000msec後の7セグの状態を1桁目の数字として取り込む
    
      delayMicroseconds(1000);
      if (digitalRead(dp_7seg) == HIGH){
        if(digitalRead(e_7seg) == LOW){
          // 8,6,2,0
          if(digitalRead(f_7seg) == LOW){
            // 8,6,0
            if(digitalRead(b_7seg) == LOW){
              // 8,0
              if(digitalRead(g_7seg) == LOW){
                number = 8;
              }else{
                number = 0;
              }
            }else{
              number = 6;
            }
          }else{
            number = 2;
          }
        }else{
          // 9,7,5,4,3,1
          if(digitalRead(f_7seg) == LOW){
            // 9,5,4
            if(digitalRead(a_7seg) == LOW){
              // 9,5
              if(digitalRead(b_7seg) == LOW){
                number = 9;
              }else{
                number = 5;
              }
            }else{
              number = 4;
            }
          }else{
            // 7,3,1
            if(digitalRead(a_7seg) == LOW){
              // 7,3
              if(digitalRead(g_7seg) == LOW){
                number = 3;
              }else{
                number = 7;
              }
            }else{
              if(digitalRead(b_7seg) == LOW){
                number = 1;
              }
            }
          }
        }
      }
    
  }
    //  numberが0でなければシリアルポートへ出力 
    if(number != 0 ){
      Serial.println( "" );
      //Serial.print( 1 );
      Serial.print( number_ten );
      Serial.print( number );
      Serial.print( "  call  " );
       
      digitalWrite(led1,HIGH);
      delay(32000 );
      digitalWrite(led1,LOW);
      number_ten = 0;
      number = 0;
    }

}

 

 unoから teratermでパソコンに取り込みます。

受信した時刻を表示させるため、”call”と受信したら、時刻を送り返すマクロをループさせて、エコーをオンにして、あたかも 時刻も受信したように見せています。


 マクロのコードです。

 

 do
    wait 'call'
    gettime time_now_str "%m/%d %H:%M"
    sendln  time_now_str

    ;doへ戻る
loop

めちゃ簡単です。



 


 

2023年1月15日日曜日

フォルダ内にある同一の画像ファイルを整理する

長年、いろんなデジタルカメラで写真を撮って、それをHDDに溜め込んでると、いつの間にか同じ画像が複数の名前で保存されてる、ってこと無いですか?

 10枚20枚くらいなら、目視で、これとこれダブってるわ、と削除できるのですが、数千枚が一つのディレクトリに溜め込んでると人間ではどうしようもありません。

今回、PYTHONを使って、HDDに溜め込んでた、JPGファイルで同じ画像を1つだけ残してあとは削除するコードを作成して、ファイルを整理してみました。

同一かどうかを判定する方法にはいくつかあるのですが、今回は、pyexiv2というモジュールを使って撮影日時が同一かどうかを調べて判定します。

 

以下の流れとなります

1 PYTHONCODEと同じディレクトリに対象の画像ファイルを置く
2  jpgファイルがあればJPGに変換する
3  JPGファイルの更新日付を読み込む
4 フォルダ内の1枚めのファイルと同じ更新日付のJPGファイルを探しファイル名をリストにする
5 リスト内に1枚残し、あとは削除用リストにコピーする

6 すべてのファイルに対して同じ操作を行う

7 削除用リストで重複が無いよう、SET型に変更する

8 削除用リスト内のファイル名のファイルを削除する

 

 

 # -*- coding: utf-8 -*-
"""
Spyder Editor

1 PYTHONCODEと同じディレクトリに対象の画像ファイルを置く
2  jpgファイルをJPGに変換する
3  jpgファイルの更新日付を読み込む
4 フォルダ内のファイルと同じ更新日付のjpgファイルを探す
5 同じならば、そのファイルを削除する
6 すべてのファイルに対して同じ操作を行う

"""
import pyexiv2
import glob
import os

"""
jpgファイルをJPGに変換する
"""
all_list_before = glob.glob('*.jpg') #
for list_before in all_list_before :
    print(list_before)
    basename_without_ext = os.path.splitext(os.path.basename(list_before))[0]
    print(basename_without_ext)
    os.rename(basename_without_ext+".jpg",basename_without_ext+".JPG")
    print(basename_without_ext+".JPG")


def date_time(jpg_file): # jpgファイルの更新日付を読み込む
    pyexiv2.set_log_level(4)
    img = pyexiv2.Image(jpg_file)
    
    metadata = img.read_exif()
    #print(metadata)
    if metadata == {} :
        time_jpg = None
    else :
        time_jpg = metadata['Exif.Photo.DateTimeOriginal']

    #print("DateTimeOriginal:", time_jpg)
    img.close()
    return time_jpg



start_list = glob.glob('*.JPG') # フォルダ内のjpgファイル名を読み込む
all_delete_list =[]
for jpg_file in start_list:
    delete_list =[]
    time_a = date_time(jpg_file)
    #print("tume_a = ",jpg_file,time_a)
    if time_a == None :
        continue
    for serch_file in start_list :
        #print("serch_file = ",serch_file)
        time_b = date_time(serch_file)
        #print("time_b  = ",serch_file,time_b)
        if time_b == None :
            continue
        if time_a == time_b :
            delete_list.append(serch_file)
    print(delete_list)
    if len(delete_list) > 1 :
        for i in range(len(delete_list)-1) :
            print(delete_list[i+1])
            all_delete_list.append(delete_list[i+1])
print(all_delete_list)
all_delete_list = list(set(all_delete_list)) # 2枚以上あれば1枚にする
print(all_delete_list)
for delete_file in all_delete_list :
    os.remove(delete_file)

完璧ではありません。

メタデータがもともと削除されてるとそのまま残ります。

またもともと壊れてるJPGファイルは手で取り除く必要があります。

でも手作業よりははるかに助かりました。