현재 회사에서 Adafruit SH1106이라는 Display 제품이라는 것을 사용하고 있다.
Adafruit SH1106 제품 사진
그리고, 제품 중에서 사용하고 있는 제품인 Adafruit MAX31865라는 제품이 있다.

 

이 제품을 동시에 사용하면서 컨트롤해야되는 상황이다.
하지만, 여기서 제대로 내용이 나오지 않고, 깨지는 현상 혹은 제대로 그래픽이 나오지 않는 현상이 발생하는데.
그것을 고칠려고 한다.

 

수정하기 전의 라이브러리는 다음과 같은 사이트에서 얻으면 된다.

 

 

 

오류라는 이유는 간단한데.
SPI는 CHIP SELECT를 이용해서 MOSI에 데이터들을 CLK에 맞춰서 받아들이게 되어 있다.
쉽게 말해서 데이터 통로는 1개이지만, CHIP SELECT로 여러개의 데이터를 받아들이는 형태이다.

 

여기서 MAX 31865의 SPI통신 방식과 SH1106의 SPI 통신 방식이 약간씩 차이가 나서 제대로된 화면이 출력이 안되거나 온도가 제대로 출력이 안되는 문제가 발생하는 것이다.
그러므로 SPI의 설정을 번갈아 가면서 처리해줘야한다.
주로 SPISettings를 이용하며, 이걸 SPITransaction이라고 한다.

 

SPITransaction이라는 것의 예제 소스를 보면 알겠지만.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <SPI.h>
 
// using two incompatible SPI devices, A and B. Incompatible means that they need different SPI_MODE
const int slaveAPin = 20;
const int slaveBPin = 21;
 
// set up the speed, data order and data mode
SPISettings settingsA(2000000, MSBFIRST, SPI_MODE1);
SPISettings settingsB(16000000, LSBFIRST, SPI_MODE3);
 
void setup() {
 // set the Slave Select Pins as outputs:
 pinMode (slaveAPin, OUTPUT);
 pinMode (slaveBPin, OUTPUT);
 // initialize SPI:
 SPI.begin();
}
 
uint8_t stat, val1, val2, result;
 
void loop() {
 // read three bytes from device A
 SPI.beginTransaction(settingsA);
 digitalWrite (slaveAPin, LOW);
 // reading only, so data sent does not matter
 stat = SPI.transfer(0);
 val1 = SPI.transfer(0);
 val2 = SPI.transfer(0);
 digitalWrite (slaveAPin, HIGH);
 SPI.endTransaction();
 // if stat is 1 or 2, send val1 or val2 else zero
 if (stat == 1) {
  result = val1;
 } else if (stat == 2) {
  result = val2;
 } else {
  result = 0;
 }
 // send result to device B
 SPI.beginTransaction(settingsB);
 digitalWrite (slaveBPin, LOW);
 SPI.transfer(result);
 digitalWrite (slaveBPin, HIGH);
 SPI.endTransaction();
}

 

SPI의 정보를 번갈아 가면서 처리하는 것을 알 수 있을 것이다.
settingsA와 settingsB을 번갈아가면서 동작해야되는게 SPI를 2가지 정보를 제대로 컨트롤 할 수 있는 방법이다.

 

여기서, OLED인 SH1106이 제대로 동작이 안된다. 라는 것을 언급했듯이.
SH1106이 제대로 동작이 되지 않는 특징이 있다.

 

그렇다면, 왜 SH1106이 동작을 하지 않는지 정확하게 어떤것이 다른지를 소스로 보고 알아보면.

 

1
2
3
4
5
#include <stdlib.h>
#include <SPI.h>
 
static SPISettings max31865_spisettings = SPISettings(5000000, MSBFIRST, SPI_MODE1);
MAX31865.cpp의 일부



1
2
3
#include "Adafruit_GFX.h"
#include "Adafruit_SH1106_changes.h"
 
SH1106.cpp의 일부
 

 

이렇게 SH1106은 SPI의 설정하는 변수가 존재하지 않는 것을 알 수 있다.

 

그리고 더 정확하게 오류나는 이유를 알려면, 메뉴얼을 보면 이해가 되는데.

 

MAX31865의 메뉴얼을 참조하면

 

다음과 같이 마지막에 failling일때, 데이터가 들어가기 때문이다.

 

SPI MODE의 표를 보고 분석한다면.

 

POL:
  • 0-클럭 idle이 low에서 출발
  • 1-클럭 idle이 high에서 출발
CPHA:
  • 0-시작 pulse에서 동작
  • 1-종료 pulse에서 동작

 

 

여기서 현상이 유사한건, MODE=1인 상태가 되어서
MAX31865는 MODE1으로 설정된 값으로 이동된다.

 

메뉴얼에서 보면, Data7부터 시작해서 8 Bit를 받고 있으므로
MSBFirst가 되는 것이다.
LSBFirst는 반대로 되어 있다.
간단하게 통신 관련되서 공부한 사람이라면, Big Endian과 Little Endian을 설정하는 거라고 보면 된다.

 

그 외 라이브러리에서 보면 알겠지만, 500kHz가 되어 있다.
이것도 메뉴얼에 살펴보면 이해가 되는데.

 

여기서 SCLK(클럭)의 최소 DC(0Hz)에서 5.0MHz라고 되어 있는것을 볼 수 있다.
이 사이에 데이터가 오갈 수 있다는 뜻이 되는것이다.

 

이유를 설정해준다고, MAX31865만을 설명을 계속 하게 된 것 같은데.
다음과 같이 SH1106의 데이터가 제대로 표시가 안되는 이유는 SPI의 설정이 달라서 일어나게 되어 있는것이다.
그래서, SH1106에 대해서 데이터를 송수신하기 위해서는 SH1106의 메뉴얼을 봐야한다.
Hz는 I²C만 제외하면 최소, 최대 Hz가 명확하게 안 밝혀져 있으니 무시하고.
이렇게 MSB가 먼저 나오는 MSBFirst인데.
Raising이 일어난 후에 데이터가 받아지는 것을 볼 수 있다.

 

즉, 데이터의 정보를 Rasing에 받도록 MODE를 설정해야되므로
MODE0가 SH1106에 적용이 된다는 것을 할 수 있다.
1
2
3
4
#include "Adafruit_GFX.h"
#include "Adafruit_SH1106.h"
 
static SPISettings SH1106_spisettings = SPISettings(8000000, MSBFIRST, SPI_MODE0);

 

다음과 같은 데이터를 설정하면 된다.
(참고로 8MHz는 SH1106의 설정을 보고 선택한 것으로 CLOCK의 분할 값이 1/2이,
AtMel2560(16MHz) 기준으로 보면, 8MHz가 맞다.)

 

이제, 이것을 데이터 송신할때마다 SPI Setting을 변경하도록 만들면 된다.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void Adafruit_SH1106::SH1106_command(uint8_t c) {
 if (sid != -1)
 {
     SPI.beginTransaction(SH1106_spisettings);
   // SPI
   //digitalWrite(cs, HIGH);
   *csport |= cspinmask;
   //digitalWrite(dc, LOW);
   *dcport &= ~dcpinmask;
   //digitalWrite(cs, LOW);
   *csport &= ~cspinmask;
   fastSPIwrite(c);
   //digitalWrite(cs, HIGH);
   *csport |= cspinmask;
   SPI.endTransaction();
 }
 else
 {
   // I2C
   uint8_t control = 0x00;   // Co = 0, D/C = 0
   Wire.beginTransmission(_i2caddr);
   WIRE_WRITE(control);
   WIRE_WRITE(c);
   Wire.endTransmission();
 }
 
}
 

 

여기서 SPI.beginTransaction()을 사용하여 SPI의 세팅을 변경해서 송신할 준비를 하도록 만드는 것이고,
SPI.endTransaction()을 사용하여 데이터를 송신하도록 하면 문제가 없게 된다.
Posted by JunkMam
,