2018년 1월 29일 월요일

clear images were obtained

5 100원 동전의 선명한 이미지를 얻다.


메쉬그리드 이미지를 얻은 후, 대전에 남아 있던 우리 멤버(유진, 동근, 기호)는 일단 큰 만족을 얻고 새벽 1시에
게스트하우스로 돌아서 편하게 눈을 붙였다.


다음날, 우리는 메쉬그리드가 아닌 좀 더 다른 이미지를 얻어보고 싶은 욕심이 생겨
다시 코셈으로 아침 6시에 출근했다.


전자현미경으로 이미지를 얻을 수 있는 것 중에 좀더 우리에게 익숙한 것이 무엇이 있을까?
고민을 한 우리는 100원짜리 동전을 우리의 전자현미경으로 찍어보기로 했다.


다음 사진은 우리 전자현미경에 찍힌 100동전 위의 이순신 장군의 얼굴이다.




위의 이순신 장군의 이미지는 설날 직전인 2017년 1월 27일에 얻은 것이다.
우리는 설에는 집인 대구로 돌아가야 했기 때문에 이때 이미지를 얻지 못했다면
이미지를 결국은 얻지 못 한채 마무리하고 대구로 내려갈 수 밖에 없었다.


하지만 이때 얻은 이미지로 7개월 간의 노력을 보상 받은 느낌이였고, 너무나 행복했다.

전자현미경의 이미지를 얻을 때는 다음과 같이 고무재질의 판으로 전자현미경 본체를 감쌌다.
이것은 외부에서 본체로 빛이 들어가면 잡음이 발생해서 정확한 이미지를 얻을 수 없기
때문이다. 즉, 전자총에서 부터 나온 전자빔에 의한 신호만 PMT에 입력되어야 하기 때문에
외부의 빛을 차단시켜 준 것이다.



다음 영상은 우리가 이 100원 동전의 이미지를 얻었을 때 찍은 동영상이다.




이때, 사실 우리도 이렇게 이순신 장군의 얼굴이 화면의 정중앙에 깨끗하게 나올 줄 몰랐다.
우리는 너무너무 행복했고 놀라웠고 뿌듯했다.


전자현미경을 함께 만들었던 5명의 친구들.
경북대학교 하드웨어 및 소프트웨어 연구 동아리 "빛돌" 이다.




원래 우리의 프로젝트는 4명이서 시작했다. (유진, 기호, 정섭, 주현)
전체 소프트웨어(GUI, 각 장비에 들어가는 모든 프로그램)는 내가 모두 맡았고,
각 하드웨어는 파트별로 나누어서 한명이 2개정도 맡아서 하였다.


이렇게 6개월을 함께 스터디를 하고 전자현미경 장비를 만들다가
계절학기가 끝난 1월에는 3명의 멤버(유진, 기호, 동근)가 전자현미경 회사인 코셈(COXEM)에 가서 3주를 노력하였다.


전자현미경은 정말 다시 생각해봐도 쉬운 프로젝트는 절대 아니다.
하지만 이것을 만든 후, 나는 그것이 첨단 장비든 , 무엇이든 간에 정확한 과학적 원리를 이해한다면 만들 수 있다는 자신감을
가지게 되었다.


대학생이 만드는 전자현미경 대회가 계속 이어지고, 전자현미경을 완성하는 팀이 많아져서 우리의 이런 자신감을 대한민국의
많은 대학생들도 가질 수 있으면 좋겠다.

2017년 10월 1일 일요일

4 영상이 나오다

[4] 영상이 나오다!!!
전자현미경의 이미지로 보기 위한 시료로써 우리는 메쉬그리드를 사용하였다.
메쉬그리드란 전자현미경의 해상도를 측정하기 위해서 쓰는 것으로 지름이 3mm인 원형 금속이다.

시료로 사용한 메쉬그리드의 크기를 100원 짜리 동전과 비교한 실물 사진이다.


이 메쉬그리드를 전자현미경의 진공관인 챔버 안에 위에 있는 시료대에 올려놓고,
전자현미경을 작동시켰다.

아래 사진들은 메쉬그리드를 시료로 했을 때의 우리가 얻은 이미지이다.
  
  
이 메쉬그리드를 더 자세하게 관찰하기 위해 렌즈에 흐르는 전류값과 스캔 코일에 흐르는 전류를 바꿔주면서 다시 촬영하였다.
그 결과 우리는 최대 확대 배율의 상을 얻을 수 있었다.




원래 제대로 찍힌다면 원 안에 격자 무늬가 보여야 하는데 배율과 초점의 문제로 격자가 관측되지 않았다.
그래서 align과 렌즈의 파라미터, 검출기의 값들을 바꿔가며 여러 번 관측했으나 격자 무늬를 볼 수는 없었다.


그리하여 동작 결과, 우리 빛돌의 대학생이 만든 전자현미경의 성능은 다음과 같이 나왔다.


화면 크기 : 21cm


시료의 크기: 3mm


배율: 70배


Pixel: 256*256


Probe size : 3 mm/256 = 11.76 um




2017년 9월 30일 토요일

[3] 전체 하드웨어 완성과 영상 찍기 시작.

1) 전체 하드웨어 구성
전자 현미경은 진공을 유지하는 곳에 전자총, 스캔, 렌즈 만 있는 것이 아니라 전원 공급기, 여러가지 회로와 진공 펌프 등이 필요하다. 그래서 우리가 만든 모든 하드웨어를 연결하고 나니 엄청 거대한 하드웨어가 탄생했다.

전자현미경 챔버 내부 모습을 자세히 보면 위에서부터 순서대로 전차종, 렌즈 두개, 스캔 코일, 검출기에 설치되어 있다.

우리가 만든 회로들!! 역시 우리는 전자과 답게 우리 전자현미경에서 구동되는 모든 회로를 우리가 손수 만든것이다.


2) 하드웨어 구동 해보기
우선 전체 하드웨어인 펌프, 전자총, 렌즈, 스캔, 검출기, 전원 공급기, 각종 회로들, 컴퓨터를 연결하여 구동해보았다.
전자 DIY를 하다보면 각각은 잘 작동을 해도, 모두 합치면 작동하지 않는 경우가 있기 때문에 조심스럽고 간절하게 시도해 보았다.

영상에서 보면 챔버 주변으로 고무 시트를 둘러주었는데 이것은 시료에서 나온 2차 전자 이외에 빛은 검출기에 들어가면 안되기 때문이다. 즉, 주변의 빛은 노이즈로 작용하기 때문에 이것을 막아주어야 한다.



3) 이미지 얻기 시도
자. 이제 이미지 얻기를 시도해보았다.
물론 한번만에 우리가 영상을 얻었다는 것은 말도 안된다. 숱한 실패가 있었는데, 아래의 영상은 영상이 나오기는 나왔으나 찌그러지게 나온 경우이다.

그리고 무언가 예상과는 다르게 노이즈가 발생하거나 작은 값의 흔들림이 존재하면 그냥 넘어가면 안된다. 반드시 이미지에 결과로 나타나게 되어 있으므로, 작은 노이즈라도 없애주어야 한다.


2017년 8월 22일 화요일

[8] 영상의 동기화 맞추기

[8] 영상의 동기화 맞추기


1) 라즈베리파이의 인터럽트 이용(실패)


아두이노 듀에서 나오는 톱니파 속도를 정해야 한다.
라즈베리에서의 인터럽트가 잘 디텍팅 되지 않는다.
저번에 했던 코드는 100ms라는 느린 펄스파를 감지한거라서 정확하게 갯수와 rising, falling이 검출되었지만 영상을 본격적으로 하기위해서 짠 아두이노 코드에서 나오는 s_p_signal은 rising을 검출하지 못했다. 또는 rising을 검출할 때는 falling을 검출하지 못함.
저번에 된 코드로 테스트 해보니 이 문제는 아두이노에서 나오는 펄스파의 속도가 빨라서 그랬던 거였음.

주기
주파수
톱니파에 맞추려면
2ms
500Hz
Rpi 인터럽터에 맞추려면
100ms
10Hz


Rpi 인터럽트에 맞추려면 결국 100ms / 512 samples = 195.3125 u/sample
약 200us 주기로 라즈베리파이에서 ADC 신호를 읽으면 된다.


-인터럽트 동기화 문제
wiringPI 라이브러리에서 제공하는 인터럽트 함수를 썻을 때 문제점이 있었다.
1)인터럽트 설정에서 INIT_EDGE_FALLING으로 설정하더라도 falling edge만 디텍팅 하는 것이아니라 rasing edge까지 디텍팅 되었다.
2)rinsing edge와 falling edge 디텍팅 사이의 시간이 100ms 정도가 있어야 정확한 (사직 한 두개 빠질때도 있음) 디텍팅이 되었다.
3)아누이노의 s_p_signal에서 발생되는 falling edge를 검출하면 바로 원하는 루틴으로 들어가서 데이터를 읽기 시작해야 하는 데 아두이노에서 falling edge가 발생한 후 몇 msec의 오랜 시간이 걸린다음에 인터럽트가 발생했다. .... 그래서 동기화가 안맞았음.
.
채널1(노란색) - 아두이노의 s_p_signal 핀 측정
채널2(파란색) - 라즈베리파이의 인터럽트 측정


=>그래서 인터럽트를 포기함.


2) while 문으로 구현


라즈베리파이의 s_p_signal 핀의 값을 계속 확인하면서 whlie문을 계속 돌다가 아두이노에서 falling edge를 보내면 원하는 루틴으로 들어가도록 하였다.
채널1(노란색) - 아두이노의 s_p_signal 핀 측정
채널2(파란색) - 아두이노의 ADC 톱니파


아두이노에서 나오는 ADC 톱니파을 위 사진에서 초록색깔 모듈인 ADC의 입력에 바로 연결하여 이미지를 만들어 보았다.
그 결과 오른쪽에서 왼쪽으로 갈수록 점점 밝아지는 이미지가 나왔다. 이것은 왼쪽으로 갈 수록 아날로그 값이 선형적으로 커지는 것을 의미하므로 정확하게 이미지를 잘 만들었다는 것을 확인할 수 있었다.


3) 미세 떨림 보정
=>하지만 조그만 흔들림이 있었다.
=>이것은 라즈베리파이가 멀티 프로세싱을 하기때문에 그 때 그때 시작하는 시간이 미세하게 달라지기 때문이다.
=>그래서 단순히 데이터를 읽고 약간의 시간 지연을 위해서 usleep()을 쓰는 것이 아니라 프로그램 내부에서 시간을 측정해서 원하는 시간이 되면 while문을 빠져나가도록 하였다.
=> 그 결과 오실로스코프로 보았을 때, 미세한 떨림이 눈으로는 확인하기 힘들 정도로 줄어들었다.  


-convert picture to C code array


원본 이미지와 받은 이미지 비교


arduino due code


//#include "smile.h"
#include "bol.h"

#define ENABLE 0
#define DISABLE 1
#define START 0
#define STOP 1
#define MAXDIGITAL 1024


#define ENABLE_PIN 7
#define DISABLE_PIN 6
#define S_P_SIGNAL_PIN 2
#define OUTPUT_PIN DAC0


volatile int enablePinState;


void makeSawtooth();
void enableISR();
void disableISR();
//void init_img(unsigned char* src);

int raw=0, col=0;
//unsigned char img[256*256] = {0};

void setup(){
//  init_img(img);
 Serial.begin(9600);
 //output
 analogWriteResolution(10);
 analogWrite(OUTPUT_PIN, 512);
 //s_p_signal
 pinMode(S_P_SIGNAL_PIN, OUTPUT);
 digitalWrite(S_P_SIGNAL_PIN, STOP);
 //enable
 pinMode(ENABLE_PIN, INPUT_PULLUP);
 pinMode(DISABLE_PIN, INPUT_PULLUP);
 attachInterrupt(ENABLE_PIN, enableISR, FALLING); //http://m.blog.naver.com/yuyyulee/220310875023
 attachInterrupt(DISABLE_PIN, disableISR, RISING);
 if(digitalRead(ENABLE_PIN)){
   enablePinState=DISABLE;
   //enablePinState=ENABLE;
   Serial.println("first state is DISABLE");
 }else{
   enablePinState=ENABLE;
   Serial.println("first state is ENABLE");
 }

}


void loop(){
 //enablePinState=ENABLE;
 while(enablePinState == DISABLE){

 }

     //speed control
   //  1) 50, 2) 50
   //  3) 10
 delay(50); //delay for STOP signal
 
 digitalWrite(S_P_SIGNAL_PIN, START);  //falling edge

 while(1){
   if(enablePinState == DISABLE){
     analogWrite(OUTPUT_PIN, 512);
     col=0;
     raw=0;
     break;
   }

   analogWrite(OUTPUT_PIN, smile[raw*256+col]*4);
   //speed control
   //  1) 440
   //  2) 220
   delayMicroseconds(220);
   col++;
   if(col == 256){
     col=0;
     break;
   }
 }
 
 digitalWrite(S_P_SIGNAL_PIN, STOP);   //rising edge
 
 analogWrite(OUTPUT_PIN, 512);

 raw++;
 if(raw==256){
   raw = 0;
 }
}





void enableISR(){
 enablePinState = ENABLE;
 Serial.println("enable");
}


void disableISR(){
 enablePinState = DISABLE;
 //enablePinState = ENABLE;
 Serial.println("disable");
}

/*
void init_img(unsigned char* src){
 int i=0, j=0;
 
 for(i=0; i<256; i++){
   for(j=0; j<256; j++){
     src[i*256+j] = j;
   }
 }
}
*/


capcher.h
#ifndef CAPCHERIMAGE_H
#define CAPCHERIMAGE_H

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <wiringPi.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <QtCore>
#include <QMutex>
#include <QThread>
#include "myspi.h"
#include "readdetector.h"
#include <sys/time.h>
#include <unistd.h>
#include "jpeglib.h"

//PIN NUMBER
#define S_P_SIGNAL_PIN 7 //pysical 7
#define SYNCHRONIZATION_PIN 25 //pysical 37

//set value
#define FRAME_NUMBER 1
#define MAXROW 512
#define MAXCOL 1000
#define DELAYSECOND 10
#define START 0
#define STOP 1
#define ERROR -1

//tread state
#define TREAD_START     0
#define TREAD_END       1
#define TREAD_RUNNING   2
#define TREAD_PAUSE     3
#define TREAD_RESUME    4
#define DISPLAY_IMAGE 5

volatile static int s_p_signal = STOP;

class CapcherImage : public readDetector
{
   Q_OBJECT



public:
   QMutex mutex;
   volatile unsigned int* gpio;
   CapcherImage(){readDetector::Init_8bit_GPIO(); m_stop = false; m_pause = false;}
   void readOneFrame(unsigned char* oneFrame);
   void init_Interrupt();
   void arduinoSAWTOOTHWAVE(int command);
   void run();
   void pause() { m_pause = true; }
   void resume() { m_pause = false; m_pauseCondition.wakeAll();}
   int makeJPEG(unsigned char* oneFrame);

private:
   bool m_stop;
   bool m_pause;
   QWaitCondition m_pauseCondition;

signals:
   void state(int treadState);
   void imageMakingProgress(int raw);
};

void interruptISR(void) ;


#endif // CAPCHERIMAGE_H

capcher.cpp
#include "capcherimage.h"

void CapcherImage::run(){

 unsigned char img[256*256]={0};

  qDebug("Image Thread start!"); //"start!!"
  emit state(TREAD_START);

  while(1)
  {
      if (m_pause)
      {
          emit state(TREAD_PAUSE); //"complete!!"
          m_pauseCondition.wait(&mutex);
          emit state(TREAD_RESUME);//"start!!"
      }

      //running code
      arduinoSAWTOOTHWAVE(ENABLE);
      readOneFrame(img);
      arduinoSAWTOOTHWAVE(DISABLE);

      emit state(TREAD_RUNNING); //"image make..."

      if(makeJPEG(img) == ERROR){
          m_pause = TRUE;
          qDebug("make image error!!");
          continue;
      }

      emit state(DISPLAY_IMAGE);

      //running code end


      m_pause = TRUE;

  }

  emit state(TREAD_END); //"complete!!"
  qDebug("Image Thread stop!");
}

void CapcherImage::arduinoSAWTOOTHWAVE(int command){
   if(command == ENABLE){
       digitalWrite(ARDUINO_ENABLE_PIN, ENABLE);
   }else if(command == DISABLE){
       digitalWrite(ARDUINO_ENABLE_PIN, DISABLE);
   }
}

void CapcherImage::readOneFrame(unsigned char *oneFrame){
   int raw = 0, col=0;
   long int start_time;
   long int time_difference;
   struct timespec gettime_now;
   int percent = 0;
   int lock = ON;
   long int in_start_time;
   long int in_time_difference;
   struct timespec in_gettime_now;

   s_p_signal=STOP;
   imageMakingProgress(percent);

   gpio_8bit_usage(ENABLE);

   //while start

   while(1){


       //measure strat time
       clock_gettime(CLOCK_REALTIME, &gettime_now);
       start_time = gettime_now.tv_nsec;


       if(digitalRead(S_P_SIGNAL_PIN) == STOP){
           lock = OFF;
       }

       if(digitalRead(S_P_SIGNAL_PIN) == START && lock == OFF){
            lock = ON;

            /*
            //measure strat time
            clock_gettime(CLOCK_REALTIME, &gettime_now);
            start_time = gettime_now.tv_nsec;
            */

            //speed control
            //     1) 500
            //     2) 30
            //usleep(30);


            //mearsure end time
            do{
                clock_gettime(CLOCK_REALTIME, &gettime_now);
                time_difference = gettime_now.tv_nsec - start_time;
                if(time_difference<0)
                    time_difference += 1000000000;
                //qDebug("msec : %lf", time_difference/1000000.0);
            }while(time_difference < 100000);



            //synchronization test
            digitalWrite(SYNCHRONIZATION_PIN, START);

            while(1){
                //measure strat time
                clock_gettime(CLOCK_REALTIME, &in_gettime_now);
                in_start_time = in_gettime_now.tv_nsec;

                //read
                oneFrame[raw*256 + col] = Read_8bit_GPIO();
                col++;

                //speed control
                //     1) 410
                //     2) 160
                //usleep(160);

                if(col == 256){

                    //synchronization test
                    digitalWrite(SYNCHRONIZATION_PIN, STOP);

                    /*
                    //mearsure end time
                    clock_gettime(CLOCK_REALTIME, &gettime_now);
                    time_difference = gettime_now.tv_nsec - start_time;
                    if(time_difference<0)
                        time_difference += 1000000000;
                    qDebug("msec : %lf", time_difference/1000000.0);
                    */

                    col =0;
                    raw++;


                    if((raw+4) % 26 == 0){
                       int percent = (raw+4)/26;
                       imageMakingProgress(percent); //take 50 usec
                    }


                    break;
                }

                //mearsure end time
                do{
                    clock_gettime(CLOCK_REALTIME, &in_gettime_now);
                    in_time_difference = in_gettime_now.tv_nsec - in_start_time;
                    if(in_time_difference<0)
                        in_time_difference += 1000000000;
                    //qDebug("msec : %lf", in_time_difference/1000000.0);
                }while(in_time_difference < 234000);

            }
        }


        if(raw == 256){
            break;
        }

    }

   //while end

    gpio_8bit_usage(DISABLE);

}

int CapcherImage::makeJPEG(unsigned char *oneFrame){
   const int image_height=256, image_width=256;
   const char *filename = "result.jpeg";

   //initialize image array
   /*
   for(int i=0; i<256*256; i++){
       oneFrame[i] = 200;
   }*/

   struct jpeg_compress_struct cinfo;
   struct jpeg_error_mgr jerr;

   FILE *outfile;
   JSAMPROW row_pointer[1];
   int row_stride;

   cinfo.err = jpeg_std_error(&jerr);
   jpeg_create_compress(&cinfo);

   outfile = fopen(filename, "wb");

   if(outfile  == NULL){
       fprintf(stderr, "can't open %s\n", filename);
       exit(1);
   }

   jpeg_stdio_dest(&cinfo, outfile);

   cinfo.image_width = image_width;
   cinfo.image_height = image_height;
   cinfo.input_components = 1;
   cinfo.in_color_space = JCS_GRAYSCALE;

   jpeg_set_defaults(&cinfo);
   jpeg_set_quality(&cinfo, 75, TRUE);

   jpeg_start_compress(&cinfo, TRUE);

   row_stride = image_width * 1;

   while(cinfo.next_scanline < cinfo.image_height){
       row_pointer[0] = &oneFrame[cinfo.next_scanline * row_stride];
       (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
   }

   jpeg_finish_compress(&cinfo);

   fclose(outfile);
   qDebug("image file is created");

   return 0;

}

void CapcherImage::init_Interrupt(){

   pinMode(S_P_SIGNAL_PIN, INPUT);
   pullUpDnControl(S_P_SIGNAL_PIN, PUD_UP);
   pinMode(SYNCHRONIZATION_PIN, OUTPUT);

/*
   if ( wiringPiISR (S_P_SIGNAL_PIN, INT_EDGE_FALLING, &interruptISR) < 0 ) {
     fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno));
     exit(-1);
   }
   */
}



void interruptISR(void) {

   char val = digitalRead(S_P_SIGNAL_PIN);
   if(val == LOW){
       s_p_signal = START;
       //qDebug("falling edge detected");
   }
   else if(val == HIGH){
       s_p_signal = STOP;
       //qDebug("rising edge detected");
   }
}






프로그램 수행 시간 측정


-clock_gettime()


채널1(노란색) - 라즈베리파이의 enable 핀 측정

채널2(파란색) - 아두이노의 s_p_signal 핀 측정

clear images were obtained