로그인회원등록 내글장바구니주문조회현재접속자
 상품 검색








 게시판 검색





 
 
회원등록 비번분실


온라인 입금계좌
ㆍ기업은행
ㆍ219-043192-01-028
ㆍ이건영

      거래은행 바로가기
 
 Sensor Applications
아듀이노 응용소스
작성자 avrtools™        
작성일 2018/03/05
첨부#1 STM32duino-DualScope-V10.ino (24KB) (Down:1)
ㆍ추천: 0  ㆍ조회: 60   
  STM32F103C ILI9341 TFTLCD Scope V2
STM32f103C Blue-Pill 기판에 Maple-Bootloader로 Upload한 STM32-Scope-V2

 기판: STM32 Blur-Pill (CPU: STM32F103C8T6) (USD 2.40)

 
개발환경: Arduion IDE 1.8.5 DUE M3 post Compiler (DUE 보드환경 추가)
개발환경 보드: STM32-duino STM32F103C8 (STM32F1XX 보드환경 추가)
개발환경 추가: https://github.com/rogerclarkmelbourne/Arduino_STM32
부트로더: Maple-mini DFU V0.1 generic_boot20_pc13.bin
부트로더 파일:
https://github.com/rogerclarkmelbourne/STM32duino-bootloader 

좌측의 4핀 커넥터는 J-Link로 부트로더 펌웨어를 굽는 SWD 연결용이다.
우측은 USB 커넥터는 micro USB로 부트로더를 Maple-mini로 변경하면 UNO처럼 편리하게 사용한다.
우측 USB 근처의 노란색 점퍼는 Boot-1과 Boot-0 점퍼로 부트로더를 변경할 때만 사용한다.
 
 J-Link 기판 (USD 5.60)
 

 STM32-2CH Scope 설명
ADC: internal 12 Bit
DAC: External I2C PCP4725 (이전 외부 DAC 정현파 구동을 참조)

점검용 스코프 소스코드로 내장 ADC 변환성능 및 아날로그 데이터의 변환품질을 확인했다.
아날로그 채널의 잡음 혹은 저장된 아날로그 배열변수의 안정도, 디지털변환 품질 등을 검사했다.

처음에는 스코프처럼 측정한 아날로그 값을 간단하게 파형으로 본다고 생각했으나, 문제가 많이 있었다.
연속변환 및 변환시작도 매끄럽지 못하고, 매번 됬다 안됬다를 반복하든가 데이터가 많이 흔들렸다.
스코프 트리거 레벨을 보니 트리거 레벨도 흔들리고 있었다. 측정값도 잡음이 많았다.

이런 모든 문제를 스코프처럼 그래픽 화면으로 보면서, 내부에 저장된 변수들을 점검하고 개선했다.
벌레잡기 이후에 안정된 트리거레벨로 검출된 파형은 깨끗하게 정지화면으로 보이고 잡음이 개선됬다.
결론은 Debug된 안정된 아날로그 값을 확보하면, 확장응용에서 안정된 데이터를 사용할 수 있다.

 V1과 V2의 차이점
PA0과 PA1의 2CH을 사용하는 것 보다, PA1과 PA3을 사용하고, PA0과 PA2는 guard로 사용했다.
이렇게 하면 PA0과 PA2에서 PA1과 PA3로 들어오는 아날로그 잡음을 제거해 준다.
트리거도 안정되게 작동하며, 검출레벨도 안정하므로 Capture 파형도 완전히 정지해서 보인다.

Triggered가 안된 화면을 Free Run으로 표시하여, Trigger가 안되는 입력상태를 볼 수 있다.
Trigger Level을 수직축에 표시해서 Trigger Level로 Trigger 작동의 불안정 원인을 쉽게 찾을 수 있었다.
Rising Edge Trigger를 구현할 때, 기준값을 -128로 offset을 설정하면 트리거점과 파형 Level이 일치된다.
-128 값은 3.3V*128/4096 =100mV 정도 낮은 값에서 trigger 하면 Trigger Level과 측정파형이 일치한다.

Time Base 값을 화면에 표시하여, 실제로 Capture 시간과 설정값의 오차를 계산할 수 있었다.
Time Base 100us으로 측정한 1KHz 간격이 1120us가 나왔다. 결국 120us이 Capture 작업으로 소비된다.
그래서 Capture 시간을 오차보상해서 1KHz 파형의 간격이 1000ms 이되도록 수정했다.

  ILI9431-TFTLCD를 사용한 STM32F103C Blue-Pill CPU 기판의 Scope-V2 시험 (터치식 USD 7.90)
 

상단에 Time Base로 측정한 Sampling Time을 표시하고, 아랫쪽에는 Grid의 간격전압을 표시했다.
좌측 중앙의 작은 빨간색 사각형은 Trigger Level을 표시한다.
가장 좋은 스코프성능은 트리거 레벨과 일치하는 파형이 간격없이 포착되고 그려져야 한다.
 
사진처럼 LCD의 중심위치인 0Vac 부근에서 정현파를 안정되게 트리거하기는 쉽지 않다.
Trigger Level 위치를 가변해서 입력전압의 최대값을 벗어나면 Trigger가 풀리고 Free Run으로 파형이 흐른다.
입력신호의 최대진폭을 살짝 벗어나도 트리거가 안되야 한다, 0Vac 부근에서 트리거가 흔들리지 않아야 한다. 

STM32F103C의 내장 ADC는 12 비트이며 2개를 내장하고 있는데,
내장 ADC 2개를 각각 PA1과 PA3에 고정으로 연결하여 병렬작동 2CH ADC을 구현했다.
ADC 변환시간 sample time은 가장 빠른 1.5us으로 설정했다.

ADC 변환종료로 DMA가 2CH ADC 값을 동시에 DMA가 자동으로 설정된 Buffer로 전송한다.
설정된 DMA Buffer의 1/2까지 DMA 전송이 끝나면 DMA 가로채기(IRQ)가 발생한다.
DMA 가로채기(IRQ) 처리함수(Handler)는 DMA 전송을 중지 시키고, Buffer를 다음 1/2로 전환한다.
주함수에서 Trigger되면 ADC를 시작시키고, ADC의 변환 종료시 매번 DMA가 작동한다.

DMA로 전송하는 Frame Buffer는 640개이고 1 CH에 320개로 TFTLCD 화면의 가로폭에 일치시겼다. 
DMA는 한번에 640 개의 ADC 데이터를 설정된 배열변수 Buffer로 자동 전송한다.
배열변수 Frame Buffer의 크기는 1280 개로 한번의 DMA전송은 1/2씩 버퍼를 전환하며 전송한다. 
1/2은 주함수에서 현재사용 중이다. 전송중인 버퍼의 파형을 억지로 보면, 중간에 끊어진 값들이 보인다.    

ADC 값은 0~4095로 TFTLCD의 세로폭 240에 표시하기 위해 ADC*240/4096으로 계산한다.
ADC 채널의 입력 Z는 1Mohm 저항을 직렬로 연결하면 3.3V/FS이 6.6V.FS로 바뀐다.
화면의 정현파는 3.3Vpp 진폭인데, 1Mohm 저항을 직렬로 연결해서 1/2 진폭으로 줄었다.
만일 이대로 1Mohm을 연결한다면, 0.5V/div 메뉴는 1.0V/div로 변경해야 한다.
 
 SPI (1)을 사용하는 TFTLCD ILI9341의 설정
사용한 2.4인치 TFTLCD는 320x240  ILI9341다. 설정은 하드웨어 SPI가 빠르다.
Library는 STM32 설치시에 포함된 Adafruit_GFX_AS.h와 Adafruit_ILI9341_STM.h 이다.
LCD 규격 : TFTLCD ILI9341 
http://www.adafruit.com/products/1651

#include "SPI.h"
#include <Adafruit_GFX_AS.h>    // 그래픽 라이브러리와 글꼴이 들어 있다.
 
#define TFT_CS  PB14   // TFT-3 =CS   임의 포트에 연결가능              
#define TFT_RST PB13   // TFT-4 =RST 임의 포트에 연결가능
#define TFT_DC  PB12   // TFT-5 =DC   임의 포트에 연결가능           
#define TFT_MOSI PB5  // TFT-6 =MOSI ->PA7 고정 포트다. (hardware SPI)
#define TFT_CLK PB3   // TFT-7 =SCK  ->PA5 고정포트다. (hardware SPI)
#define TFT_LED PB15  // TFT-8 =LED ->3.3V 전원에 연결
#define TFT_MISO PB14 // TFT-9  =MISO는 사용하지 않는다. 대부분 LCD는 그리기 전용이다.

#include <Adafruit_ILI9341_STM.h>  // ILI9341를 SPI로 연결하는 보조함수이다.
Adafruit_ILI9341_STM tft =Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST); // hardware SPI                

 SPI 2를 사용하자
SPI 1 근처에는 일반적인 아날로그 프로젝트에서 많이 사용하는 핀들이 많이있다.
그래서 PB8~PB15를 이용하는 SPI 2로 전환하기로 했다.
STM32duino의 자료도 별로 없는데, SPI-2로 ILI9341를 연결하는 자료는 더 찾기 어렵다.
 
그러나 SPI 라이브러리 안에, 숨어있는 함수가 있었다.
일반적으로 #include "SPI.h"를 사용하면, SPI 1으로 포트가 고정되어 있다.
다른 핀으로 바꾸려면 SPIClass SPI_2 (2)라는 AF(포트의 2번째 기능) 설정이다.

별도로 정의하지 않지만, 이미 SPIClass SPI_1 (1)로 설정되어 있다.
SPIClass SPI_1 (1)
#define SPI_SS  PA4  
#define SPI_SCK  PA5
#define SPI_MISO PA6
#define SPI_MOSI PA7
 
SPI 2를 사용하면 SPI 라이브러리에 이미 다음과 같이 설정이 되어있다.
SPIClass SPI_2 (2)
#define SPI_SS  PB12  
#define SPI_SCK  PB13
#define SPI_MISO PB14
#define SPI_MOSI PB15
 
또한 1.8~2.8 인치 TFTLCD인 ILI9341를 STM32에서 사용하려면 Adafruit_ILI9341_STM.h가 필요하다.
여기서 ILI9341의 연결포트와 SPI 제어함수는 SPI.h에 있다.
LII9341는 하드웨어가 SPI 포트를 사용하므로, SPI 라이브러리를 SPI 2로 설정하면 된다.

 SPI 2의 실제 코드
// 1. 다음 LCD 핀은 사용자가 임의로 설정할 수 있다. 그러나 SPI 핀 근처가 좋다.
#define TFT_CS  PB12  // pin3 SS you may change               
#define TFT_RST PB8   // pin4 RST you may change
#define TFT_DC  PB9   // pin5 DC you may change
 
// 2. 다음 LCD핀은 SPI 2를 사용하면 이미 CPU의 구조에서 결정된 포트이므로 변경이 불가능하다.
#define TFT_MOSI PB15 // pin6 MOSI (fixed in SPI library)
#define TFT_SCK PB13  // pin7 SCK (fixed in SPI library)
#define TFT_MISO PB14 // pin9 MISO (fixed in SPI library)
MISO는 LCD에서 나오는 데이터이므로 연결하지 않는다. 그러나 PB14는 다른 용도로 사용하지 못한다.
 
// 3. SPI 2로 LCD 핀을 설정하고 다음과 같이 LCD 라이브러리를 정의 한다.               
#include <Adafruit_GFX_AS.h>    // Core graphics library, with extra fonts
#include <Adafruit_ILI9341_STM.h> // SPIClass SPI_1(1) connection but used SPI_2
Adafruit_ILI9341_STM tft=Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST); // use hardware SPI-2
 
// 4. 이제 SPI 2를 사용하려면, 다음과 같이 새로 정의를 해야 한다,
#include "SPI.h"
SPIClass SPI_2 (2); // 2번째 SPI 포트를 사용한다
 
//5.  setup() 안에 다음과 같이 SPI 2와 LCD를 초기화 한다.
void setup()  
{
  SPI_2.begin(); // LCD를 SPI 2로 설정한다
  SPI_2.setClockDivider(SPI_CLOCK_DIV4);  // SPI clock 72MHz/4 =18MHz
  
  tft.begin(SPI_2, 18000000); // SPI 클래스는 (2). SPI clock은 18MHz
  tft.setRotation(1);  // 화면을 가로로 90도 돌린다.
  tft.fillScreen(ILI9341_BLACK);  // 전원인가시 그래픽 RAM에 들어간 쓰레기값을 지운다.
  tft.setTextSize(2);  // 글꼴 크기를 2로 한다 (16x16)
  
  tft.setTextColor(cTEXT);  // 문자색은 밝은 나무색
  tft.setCursor(0, 0);  // 처음 문자표시 위치
  tft.println("LCR METER V1.0 ACLEDS INC.");  // 제목을 표시한다
  delay(2000);  // 제목 표시시간은 2초
  tft.fillScreen(ILI9341_BLACK);  // 그림을을 그리기 위해 화면을 지운다
}

 다음은 LCD 화면을 정의한다
// Screen dimensions 화면크기
int16_t myWidth =320;
int16_t myHeight =240;
 
// ILI9341 TFTLCD 색상
#define cADC1 0xFFFF // ILI9341_WHITE 백색 (ADC1 CH1 =ADC1)
#define cADC2 0xFFE0 // ILI9341_YELLOW 황색 (측정 CH2 =ADC2)
#define cGRID 0x03EF // ILI9341_DARKSYAN 하늘색 (눈금 GRID)
#define cERASE 0x0000 // ILI9341_BLACK 검정색 (바탕색)
#define cTEXT 0xCCCC  // ILI9341_WOOD 나무색 (글씨색)
 
  STM32-Scope 장치설정
void setup()  

  pinMode(PA0, OUTPUT);  // guard
  pinMode(PA1, INPUT_ANALOG);  // ADC CH1
  pinMode(PA2, OUTPUT);  // guard
  pinMode(PA3, INPUT_ANALOG);  // ADC CH2
  pinMode(PA4, OUTPUT);  // guard
  
  digitalWrite(PA0, LOW);
  digitalWrite(PA2, LOW);
  digitalWrite(PA4, LOW);
 
  pinMode(PC13, OUTPUT);  // ob board LED
  digitalWrite(PC13, HIGH);  // off LED
                
  usb.begin();  // 직렬통신 포트는 USB로 변경 (PA11, PA12)
  usb.println();  // dummy for usb
 
  init_ADC2(); //  ADC1, 2 설정
  start_ADC2(); // ADC 시작 
    
  SPI_2.begin(); //Initiallize the SPI 2 port.
  //SPI_2.setClockDivider(SPI_CLOCK_DIV16); // Slow speed (72MHz/16 =4.5MHz) 저속용.
  tft.begin(SPI_2, 18000000); // SPI는 2번, SPI 속도 18MHz <-- 고속용을 사용한다.

  tft.setRotation(1); // 화면을 가로로 돌린다
  tft.fillScreen(ILI9341_BLACK); // 화면을 지운다
  tft.setTextSize(2); // 문자 크기는 2
  
  tft.setTextColor(cTEXT);  // 문자색 =wood
  tft.setCursor(0, 0);  // 문자 시작위치
  tft.println("STM32-scope V1.0");  // 제목 표시
  delay(2000);  // 표시시간 2초
};

 STM32 2CH 주 함수
LCD는 빠른 간격으로 update를 하면 화면의 Contrast가 내려간다.(화면이 흐려진다) 
그래서 ms 타이머 millis()를 사용하여 100ms 마다 ADC를 읽고, 새로운 파형으로 교체한다.
 
uint32_t reflashTimer; // 화면 갱신 타이머
uint32_t trigTimer;   // 트리거 포기시간 타이머
uint32_t trigBox =2048; // 트리거 레벨 =0Vac
#define updateTime 100  // 화면 갱신주기 =100ms
#define skipTime 100  // 트리거 포기시간 =100ms
#define triggEdge 128  // 트리거 감지폭 =3.3V*32/4096 =100mV
 
void loop() // 주함수 main()의 시작
{
  control_Key();   // 조작 스위치를 읽는다, 화면에 변경된 내용을 표시한다. 
  if ((millis() -reflashTimer) >updateTime) // 화면갱신 시간인가?
  {
    reflashTimer =millis();   // 화면갱신 타이머를 리셋 
 
    notTriggered =true;   // 트리거 안된 상태로 전환 =새로운 트리거 검출을 시작   
    if (triggerType ==1) triggerNegative();  // -edge 트리거 검출 함수로 간다
    else triggerPositive();  // +edge 트리거 검출 함수로 간다
 
    if (! notTriggered)   // 트리거 됬는가 ?
    {
      start_ADC2();      // ADC를 시작한다, ADC는 DMA를 부른다.
      digitalWrite(BOARD_LED, LOW); // 트리거 LED를 켠다
 
      samplingTime =micros();   // sample time을 리셋
      while (dma1_ch1_Active);  // DMA 전송 완료 ?
      samplingTime =(micros() -samplingTime); // sample time을 저장 (화면에 표시)         
      digitalWrite(BOARD_LED, HIGH); // 트리거 LED를 끈다
     
      // 포착된 화면 그리기
      erasePlot(cERASE); // 먼저 그린(plot) 파형을 지운다 
      showGrid(); // Grid를 그린다
      plotADC2(); // 새로운 파형을 그린다  (트리거된 파형) 
    }
 
    else  // free run (트리거 안됨)
    {
      start_ADC2();      // ADC를 시작한다, ADC는 DMA를 부른다.
      samplingTime =micros();   // sample time을 리셋  
      while (dma1_ch1_Active);  // DMA 전송 완료 ?
      samplingTime =(micros() -samplingTime);  // sample time을 저장 (화면에 표시)
        
      erasePlot(cERASE); // 먼저 그린(plot) 파형을 지운다 
      showGrid(); //  Grid를 그린다
      plotADC2(); // 새로운 파형을 그린다 (트리거 안된 파형)
    }
  }
}; // 주함수의 끝
 
 STM32-Scope의 보조함수
보조함수의 초기화 함수들은 V1.0과 같다(STM32-scope V1.0 참조)
 
 +에지 트리거 검출
고속 ADC 연속변환에서 ADC는 처음 변환값이 안나온다. 3번째 값 부터 안정된다.
void triggerPositive()
{
  triggerPoints[0] =analogRead(analogInPin1); // remove 1st data
  triggerPoints[0] =analogRead(analogInPin1); // remove 2nd vlaue
  triggerPoints[0] =analogRead(analogInPin1); // 1st level
 
  trigTimer =millis();
  while(notTriggered)
  {
    triggerPoints[1] =analogRead(analogInPin1); // remove 1st value
    triggerPoints[1] =analogRead(analogInPin1); // remove 2nd value
    triggerPoints[1] =analogRead(analogInPin1); // 2nd level
   
    if ((triggerPoints[0] <triggerValue -triggEdge) && (triggerPoints[1] >triggerValue) )
    {
      notTriggered =false;
    }
    if ((millis() -trigTimer) >skipTime)
    {
      notTriggered =true;
      return; // over time ?
    }
    triggerPoints[0] =triggerPoints[1]; // make new 1st level
  }
};
 
 -에지 트리거 검출
void triggerNegative()
{
  triggerPoints[0] =analogRead(analogInPin1); // remove 1st data
  triggerPoints[0] =analogRead(analogInPin1); // remove 2nd vlaue
  triggerPoints[0] =analogRead(analogInPin1); // 1st level
 
  trigTimer =millis();
  while(notTriggered)
  {
    triggerPoints[1] =analogRead(analogInPin1); // remove 1st value
    triggerPoints[1] =analogRead(analogInPin1); // remove 2nd value
    triggerPoints[1] =analogRead(analogInPin1); // 2nd level
 
    if ( (triggerPoints[1] >triggerValue+triggEdge) && (triggerPoints[0] <triggerValue) )
    {
      notTriggered =false;
    }
    if ((millis() -trigTimer) >skipTime)
    {
      notTriggered =true;
      return; // over time ?
    }
    triggerPoints[0] =triggerPoints[1]; // make new 1st level
  }
};
 
 지우기
속도를 위해, 먼저 그린 그래프만 선택적으로 지운다,
sampleTime도 변경된 경우에만 지운다,


//====================================================================================
// use ploted to improve erase speed   
//====================================================================================
uint16_t plot[320]; // myWidth =320
float sampleTimeLog;
uint16_t triggerLog;
uint16_t xscaleLog;
 
void erasePlot(uint16_t removeColor)
{
  tft.fillRect(1, 1, 5, 238, cERASE); // erase trigger mark area
  tft.drawFastHLine(5, myHeight -(triggerLog *myHeight/4096), 315, cERASE); // erase trigger level 
 
  if ((sampleTimeLog !=samplingTime) || (xscaleLog !=xScale))
  {
    sampleTimeLog =samplingTime;  // store new value
    xscaleLog =xScale;
    tft.fillRect(2, 2, 118, 18, removeColor);   // erase menu at 1st line
  }
  if (triggerLog !=triggerValue)
  {
    triggerLog =triggerValue;   // store new trigger value
    tft.fillRect(2, 218, 318, 18, removeColor);   // erase menu at last line
  }
 
  for (uint16_t sigX =2; sigX <myWidth-2; sigX +=2)
  {
    tft.drawLine (sigX-2, 239-plot[sigX-2], sigX+0, 239-plot[sigX+0], removeColor);  // erase ADC1
    tft.drawLine (sigX-1, 239-plot[sigX-1], sigX+1, 239-plot[sigX+1], removeColor);  // erase ADC2
  }
};

 GRID는 어두운 색으로 그린다.
//====================================================================================
// draw Grid
//====================================================================================
void showGrid() 
{
  // draw grid box for 40x40 dots
  tft.drawRect(0, 0, myWidth, myHeight, cGRID);   // 1st draw to most outline box
  for(int y =0; y <myHeight; y +=40) tft.drawFastHLine(0, y, myWidth, cGRID); // H axis
  for(int x =0; x <myWidth; x +=40) tft.drawFastVLine(x, 0, myHeight, cGRID); // V axis
 
  // draw 2x16 box at trigger level
  tft.fillRect(1, myHeight-8 -(triggerValue *myHeight/4096), 4, 16, ILI9341_RED);
  tft.drawFastHLine(5, myHeight -(triggerValue *myHeight/4096), 315, ILI9341_BLUE);
     
  // time base =(smplingTime*(xScale-2)) /197
  tft.setCursor(8, 4);
  tft.print(samplingTime*xScale/197, 0);     
  tft.print("us/div");     
 
  tft.setCursor(8, 220);
  tft.print("0.5V/div"); 
  tft.setCursor(190, 220);
  tft.print("trig ");
  tft.print(triggerValue*3.3/4095, 2);
  tft.print("V");
};
 
 DMA 전송된 배열변수
dataPoints[1280]에서 640 개(1/2은 DAM 전송중)를 2CH로 320개씩 그린다.
고속 ADC변환으로 DMA  전송된 ADC 값은 dataPoints[1280]에 들어 있다.
이중에 dataPoints는 [0~639]와 [640~1279] 중에서 하나를 선택하는 pointer 이다.
 
각각의 1/2 buffer에는 320개의 ADC1, ADC2, ADC1, ADC2,,,의 순서로 640의 ADC값이 저장되어있다.
그러므로 짝수 pointer는 ADC1의 값이고, 홀수 pointer는 ADC2의 값이다.
 
또한  고속 ADC 전송에서 2개의 ADC 입력을 Ch1과 ch2의 핀으로 연결하는 Analong 스위치의 속도로 비어있다.
그러므로 dataPoints[0~1] 값은 사용하지 못하고 [2~3]의 값을 강제로 읽어다 저장한다.
그래야 plot 함수에서 첫번째 값이 화면의 상단이나 하단부터 출발하지 않고, 자기위치에서 plot를 시작한다.
 
X축 scale을 button 스위치로 조절하면 이함수에서 dataPoints[]의 값을 Xscale 만큼 건너뛰기 한다.
예를 들어 Xscale이 1이면 320개x2ch = 640개의 값을 [0][1]~[638][639] 까지 plot한다.
Xscale이 10 이면 [0][1],[10][11],[20][21]~~으로 건너뛰면서 ADC 값을 plot한다,

xsacle의 값은 1~24 까지다. 24 이상을 보면 datapoints[1279] 이상의 다른 변수값들이 plot된다.
그런데 datapoints[]의 1/2만 사용하고 나머지는 DMA가 사용중인데 Xscale을 24 까지 plot할 수 있나?
주함수에서 plot 할 때면, DMA는 빠르기 때문에 전송중인 2/2 datapoint[]도 거의 완성되었기 때문이다.
그리고 DAM 전송이 완료되지 않았더라도, dataPoints의 2/2는 이전에 포착된 동일한 파형이기 때문이다.

//====================================================================================
// draw ADC1 and ADC2, values not available dataPoints[0~1]
//====================================================================================
void plotADC2()
{
  uint16_t Even =0;

  //calculate first sample for 1st draw point
  if (xScale %2 ==0) Even =1; //  match data position of dma buffer for ADC2
  plot[0] =dataPoints[2*xScale+0] *240/4096;  // ADC1 ch1 make 1st point same as 2nd point
  plot[1] =dataPoints[3*xScale+Even] *240/4096; // ADC2 ch2 make 1st point same as 2nd point
 
  for (uint16_t sigX =2; sigX <myWidth-2; sigX +=2)   // odds are ADC1, evens are ADC2
  {
    plot[sigX+0] =dataPoints[(sigX+0) *xScale+0] *240/4096; // store value to plot ADC1
    plot[sigX+1] =dataPoints[(sigX+1) *xScale+Even] *240/4096; // store value to plot ADC2
   
    tft.drawLine (sigX-2, 239-plot[sigX-2], sigX+0, 239-plot[sigX+0], cADC1); // odd =ADC1
    tft.drawLine (sigX-1, 239-plot[sigX-1], sigX+1, 239-plot[sigX+1], cADC2); // even =ADC2
  }
};

 최종 수정본 작동 사진
 
 
 
트리거 레벨을 확인하기 위해 Trigger Level 보조선을 청색으로 추가했다.
PC15와 PC14 핀에 button 스위치를 연결하여 Trigger Level Up과 Down으로 설정했다.
입력 스위치 port는 input_pullup 기능으로 외부저항이 없이 HIGH로 된다.
그러므로 4개의 button 스위치는외부 pullup 저항없이 스위치만 GND로 연결하면 된다.

Button 스위치의 scan 시간은 50ms으로 모두 꺼진다음 새로 눌릴 때 edge를 검출한다,
Trigger Level 값을 조절하여 화면에 보조선도 그리고, Analog 파형의 진폭과 비교하여 Trigger 한다.
그리고 우측 하단에 trigger level의 전압을 표시하여 픅정한 파형의 전압을 확인할 수 있게 했다.
 
PB1과 PB0도 Button 스위치를 연결하여, X축의 scale을 1~1/24 까지 압축해서 낮은 주파수를 볼수 있다.
역시 PB1과 PB0는 X축의 sacle을 검출할 때, 50ms 간격으로 눌림 edge를  검출한다.
 
 4개의 button 스위치 검출함수
seup()의 상단에 추가된 GPIO 설정을 추가한다. 입력핀은 INPUT_PULLUP 한다.


void setup()  
{
  pinMode(PB0, INPUT);  // xScale up
  pinMode(PB1, INPUT);  // xScale down
  pinMode(PB0, INPUT_PULLUP);
  pinMode(PB1, INPUT_PULLUP);
 
  pinMode(PC14, INPUT); // trigger level down (step =1/10 grid)
  pinMode(PC15, INPUT); // trigger level up (step =1/10 grid)
  pinMode(PC14, INPUT_PULLUP);
  pinMode(PC15, INPUT_PULLUP);
 
  pinMode(PA0, OUTPUT);  // guard
  pinMode(PA1, INPUT_ANALOG);  // set up adc1
  pinMode(PA2, OUTPUT);  // guard
  pinMode(PA3, INPUT_ANALOG);  // set up adc2
  pinMode(PA4, OUTPUT);  // set up adc2
 
  digitalWrite(PA0, LOW);
  digitalWrite(PA2, LOW);
  digitalWrite(PA4, LOW);


  pinMode(PC13, OUTPUT);  // trigger LED
  digitalWrite(PC13, HIGH);  // off LED
 
 // 이하 생략, 이전 글 STM32-scope V1 참조
};
 
 주 함수 상단에는 다음과 같이 스위치 제어 함수를 호출한다. 
void loop()
{
  control_Key();  // trigger level control and xscale control

 그리고 button 제어 함수는 다음과 같이 Xscale과 Trigger로 분리하여 호출한다.
50ms 타이머는 ms 타이머 millis()값을 저장하여 비교한다. 시간이 되면 4개의 button switch를 scan한다.
 
//====================================================================================
// PC15 =trigger Level up (+4) max =4095
// PC14 =Trigger Level down (-4) min =0
// PB1 =Xscale up (+1) max =24
// PB0 =Xscale down (-1) min =1
//====================================================================================
uint8_t trigKey =0; // trigger key status
unsigned long keyTimer =0; // key timer
uint8_t xscaleKey =0; // xscale key status
unsigned long xScale =22;  // xscale =1~24 (11 =250us/div, 22 =500us)
 
void control_Key()
{
  if (keyTimer -millis() >50) // key sacn =50ms interval ?
  {
    keyTimer =millis(); // reset timer Key
   
    trigg_Key();  // trigger level control
    xscale_Key(); // X-axis scale control  
  } // end of timerKey
};
 
 다음은 Trigger Level을 조절하는 trigger up-down 제어함수다.
//====================================================================================
// trigger level control
//====================================================================================
void trigg_Key()   // trigger key control
{
  if( (digitalRead(PC15) ==1) && (digitalRead(PC14) ==1) )
  {
    trigKey =1; // all off key?
    return;
  }
 
  if ( (trigKey ==1) && (digitalRead(PC15) ==0) ) // up key =on ?
  {
    triggerValue +=(4*4096/myHeight);  // trigger level up by 1/10 grid
    if (triggerValue >4095) triggerValue =4095; // limit max
    trigKey =0;
    return;
  }
 
  if ( (trigKey ==1) && (digitalRead(PC14) ==0) ) // down key =on ?
  {
    triggerValue -=(4*4096/myHeight);  // trriger level down by 1/10 grid
    if (triggerValue <0) triggerValue =0; // limit min
    trigKey =0;
    return;
  }
};
 
 다음은 Xscale up-down 제어함수다.
//====================================================================================
// X-scale key control
//====================================================================================
void xscale_Key()   // X-scale key control
{
  if( (digitalRead(PB1) ==1) && (digitalRead(PB0) ==1) )
  {
    xscaleKey =1; // all off key?
    return;
  }
 
  if ( (xscaleKey ==1) && (digitalRead(PB1) ==0) ) // xscale up key =on ?
  {
    if (xScale >23) xScale =23; // limit max
    xScale++;  // xScsale +1
    xscaleKey =0;
    return;
  }
 
  if ( (xscaleKey ==1) && (digitalRead(PB0) ==0) ) // xscale down key =on ?
  {
    if (xScale <2) xScale =2;   // limit min
    xScale--;   // xScale -1
    xscaleKey =0;
    return;
  }
};

 소감
Analaog 값의 측정은 경험이 많아도 쉽지 않다. 한번 ADC 변환값이 읽혀진다고 된게 아니다.
이 프로젝트를 개발하면서 Analog 변환의 문제는 원인이 여기저기 많이 있다는 경험을 했다.
Analog 측정값의 품질을 확인하려면 내장 scope함수를 사용하면 매우 편리하다. 

이제 STM32duino 기판도 UNO 처럼 Arduino IDE에서 안정된 작동을 한다.
Upload도 별도의 tool 이나 ISP가 없어도 부트로더만 변경하면 STM32F103C 기판의 USB 포트로 upload 된다.
reset 스위치를 누르거나 boot 점퍼를 옮기지 않고도 UNO 처럼 자동으로 upload 된다.

data를 usb 포트로 전송하는 것도 serial.Print()를 us,print()로 바꾸면 간단하게 사용할 수 있다.
별도의 serial port가 있으므로 serial.Print() 함수로 modem 이나, serial LCD로 연결할 수 있다.
 
 개조전 참조한 소스

STM32 O-scope는 1CH 뿐이고, 코드를 설명하는 주석도 거의 없으며, button 스위치도 없다.
게다가 정현파를 입력하면 트리거도 안되고, 측정된 파형은 잡음도 많고 심하게 흔들린다.
참조: 
http://stm32duino.com/viewtopic.php?f=19&t=107&p=1202#p1194

 이 펌웨어 소스를 응용한 장치의 개발이나, 주문형 펌웨어가 들어간 모듈 주문 받습니다.
 주의 : 이 자료의 무단 복제 및 무단 배포를 금지합니다.
이 프로그램은 무료 소프트웨어로, 신체와 재산 상의 어떤 위험과 손해를 보상하지 않습니다.
이 프로그램은 GNU 무료 소프트웨어 배포규정을 따릅니다.
 AVRTOOLS™
   
윗글 STM32F103C 12비트 정현파 DDS V2
아래글 STM32F103C 32비트 ARM-CPU 2채널 Scope
    N         제목    글쓴이 작성일 조회 추천
아듀이노 응용소스 게시판 avrtools™ 2016/02/05 (금) 346 0
41 STM32F103C 12비트 정현파 DDS V2 avrtools™ 2018/03/08 (목) 43 0
40 STM32F103C ILI9341 TFTLCD Scope V2 avrtools™ 2018/03/05 (월) 60 0
39 STM32F103C 32비트 ARM-CPU 2채널 Scope avrtools™ 2018/02/25 (일) 66 0
38 STM32F103C의 ILI9341 ILI9163 TFT-LCD 연결방법 avrtools™ 2018/02/19 (월) 74 0
37 STM32F103C+MCP4725 DDS 1KHz 정현파 발생기 avrtools™ 2018/02/18 (일) 76 0
36 STM32F103C 기판의 1~8CH ADC DMA 전송 avrtools™ 2018/02/17 (토) 74 0
35 STM32F103C 기판의 SSD1306 OLED 구동 avrtools™ 2018/02/16 (금) 74 0
34 STM32F103C ARM32 기판의 독립 IDE 소개 avrtools™ 2018/02/14 (수) 83 0
33 STM32F103C ARM32 기판의 Bootloader 개조 avrtools™ 2018/02/14 (수) 89 0
32 ESP32+OLED 기판과 PWM generator avrtools™ 2018/02/11 (일) 76 0
31 Wemos-Lolin32 Audio FFT Analyzer 제작 avrtools™ 2018/02/07 (수) 75 0
30 DUE SAM3X8E Audio FFT Analyzer 제작 avrtools™ 2018/01/30 (화) 85 0
29 AD5933 LCR-Impedance Analyzer 제작 avrtools™ 2012/03/17 (토) 605 0
28 ESP-01 펌웨어 업그레이드와 WiFi 2 Relay Control avrtools™ 2017/12/24 (일) 80 0
27 ESP12E-devKit로 만드는 WiFi 4 Relay 제어장치 avrtools™ 2017/12/23 (토) 79 0
26 M328-mini로 만드는 Touch 용량검출센서 avrtools™ 2017/12/19 (화) 79 0
25 ESP8266 MQTT Relay Control avrtools™ 2016/03/03 (목) 534 0
24 2 채널 ESP8266 WiFi Switch의 제작 avrtools™ 2016/02/25 (목) 635 0
23 ESP-12E SDK 0.9.5 사용방법 avrtools™ 2016/02/18 (목) 597 0
22 ESP8266 ESP-12E WiFi 센서 서버의 제작 avrtools™ 2016/02/17 (수) 564 0
21 Arduino DS3231 RTC to 5110 LCD avrtools™ 2016/02/16 (화) 575 0
20 ESP8266 Weather Server의 제작 avrtools™ 2016/02/15 (월) 598 0
19 Arduino 온습도 센서 DHT-22 avrtools™ 2016/02/12 (금) 497 0
18 ESP8266 WiFi 펌웨어 업그레이드 avrtools™ 2016/02/11 (목) 769 0
17 Arduion ESP8266 WiFi 설정 방법 avrtools™ 2016/02/10 (수) 752 0
16 Arduino 정전용량식 수분센서의 분석과 제작 avrtools™ 2016/02/07 (일) 416 0
15 Arduino 전극식 수분센서의 분석과 제작 avrtools™ 2016/02/07 (일) 554 0
14 Arduino 정밀 전력계의 ADC avrtools™ 2016/02/02 (화) 818 0
13 Arduino 정밀 전력계의 LPF avrtools™ 2016/02/02 (화) 563 0
12 Ardunio 16비트 ADC Data Logger avrtools™ 2016/01/31 (일) 444 0
11 Arduino AC/DC Power Meter의 제작 avrtools™ 2016/01/29 (금) 862 0
10 Arduino 교류 역율계(power factor)의 제작 avrtools™ 2016/01/29 (금) 572 0
9 Arduino DUE Pezo-SPeaker LCQ Meter 소스 avrtools™ 2016/01/24 (일) 370 0
8 QTouch ADC 근접검출 스위치 avrtools™ 2016/01/21 (목) 598 0
7 Arduino 음성인식 Speech/Voice Recognition avrtools™ 2013/09/14 (토) 1809 0
6 Arduino Uno로 만드는 3축 CNC avrtools™ 2013/09/10 (화) 2736 0
5 Arduino로 만드는 mySpectral 분광기 avrtools™ 2013/09/04 (수) 2259 0
4 8채널 12비트 ADC MCP3208 오실로스코프 avrtools™ 2012/03/29 (목) 639 0
3 교류저항 (impedance) 측정 AD5933 avrtools™ 2012/03/17 (토) 597 0
2 Arduino DMX512 수신기 제작 avrtools™ 2012/03/15 (목) 3830 0
1 TSL2561 조도 측정기의 제작 avrtools™ 2011/09/11 (일) 2814 0
1

바구니 : 0
 보관함 : 0
오늘뷰 : 0
HOME   |   회사소개   |   제휴안내   |   회사위치   |   서비스이용 약관   |   개인정보 보호정책   |   사이트맵
17015 경기도 용인시 기흥구 동백중앙로16번길 16-25, 508호. 전화 : 031-282-3310
사업자 등록번호 : 697-47-00075 / 대표 : 이건영 / 업태 : 제조업 / 종목 : LED조명, LED전원, 제어장치.
개인정보 관리책임자 : 홈페이지 관리자 . Copyright ⓒ2016 아크레즈 (ACLEDS INC.)
HOME TOP PREVNEXT 0 0 0