출처: https://rs29.tistory.com/13
사용한 MAX7219 7-sement 모듈
- 통신 방법 : SPI (최대속도 : 10Mhz)
- 작동 전압 : 4.0~5.5
- 환경 : P-NUCLEO-WB55 개발 보드, Atollic TrueSTUDIO
[동작]
- 16 비트 데이터 포맷 사용 (D15~D12 사용 X)
- D11~D8 ADDRESS(명령어 역할), D7~D0 DATA(MSB to LSB, 해당 명령어 설정값)
- 작동 시작 됐을 때, 모든 컨트롤 레지스터는 리셋, 디스플레이는 비어있는 상태이고 MAX7219는 셧다운 모드에 진입한다
- 디스플레이를 사용하기 전에 디스플레이 드라이버를 프로그래밍해야한다
- 그렇지 않으면, 첫번째 자리를 스캔하도록 설정되고 데이터 레지스터의 데이터를 디코딩하지 않으며 밝기 레지스터는 최소값으로 설정된다
– 레지스터 어드레스 맵
– 디코드 모드
- 디코드 모드 레지스터는 BCD 코드 B(0-9, E, H, L, P, -)를 설정하거나 각 자리에 대해 디코드 미사용 설정
- 레지스터의 각 비트는 디스플레이 숫자 자리에 해당 (D0=디스플레이 첫째 자리, D1=둘째 자리, …)
- 로직 하이(1)는 Code B 디코딩을 선택하고 로직 로우(0)는 디코더를 우회한다
- 코드B 디코드 모드가 사용될 때, 디코더는 숫자 레지스터 데이터의 하위 니블(D3-D0)만을 보고 D4-D6 비트는 무시한다
- 소수점을 설정하는 D7은 디코더 설정에 대해 독립적이고 로직 1일 때 소수점이 켜진다
- 디코드 미사용 설정을 한 뒤, 해당 디스플레이 자리의 데이터 비트 D7-D0은 MAX7219의 세그먼트 라인에 해당
- (ex : 0x0900 을 전송해 디스플레이 모든 자리를 디코드 미사용으로 설정하고 0x0130 (Digit 0+Segement Line B,C)을 전송하면 디스플레이 첫번째 자리에 세그먼트 라인 B, C가 켜진다)
– 밝기 조절
- 디스플레이 밝기는 Intensity 레지스터를 통해 디지털 방식으로 조절할 수 있다
- 디스플레이 밝기의 디지털 컨트롤은 intensity 레지스터의 하위 니블로 제어되는 내부 PWM을 통해 제공된다
- 모듈레이터 스케일은 RSET에 의해 설정된 최대 전류의 최대 31/32에서 1/32까지 16단계로 평균 세그먼트 전류를 조정한다
– 스캔 리미트 레지스터
- 스캔 리미트 레지스터는 디스플레이의 1~8번째 자리 중 몇개의 자리를 사용할지를 설정한다
- 스캔된 숫자의 갯수는 디스플레이 밝기에 영향을 주기 때문에 스캔 리미트 레지스터를 디스플레이의 여백 부분으로 사용해서는 안된다 (선두의 의미없는 0을 감추는 것처럼)
- 표에서 보이는 것처럼 두번째 이상의 자리를 사용하면서 하위 자리의 디스플레이를 미사용하는 것은 불가능하다
- (사용 설정을 한 뒤, 해당 자리를 공백으로 채우는 것과는 다름)
– 디스플레이 테스트 레지스터
- 디스플레이 테스트 레지스터는 테스트 모드, 노멀 모드 설정을 할 수 있다
- 디스플레이 테스트 모드는 모든 컨트롤 및 자리 레지스터(셧다운 레지스터 포함)를 변경이 아닌 재정의 해서 모든 LED를 켠다
- 디스플레이 테스트 모드에선 8자리가 스캔되고 31/32 듀티 사이클을 가진다
- 디스플레이 테스트 레지스터가 노멀 동작으로 재설정되기 전까지는 테스트 모드 유지
– No-Op 레지스터
- No-Op 레지스터는 MAX7219를 직렬로 연결할 때 사용
- 모든 디바이스의 LOAD/CS 인풋을 묶어 연결하고 DOUT을 인접한 디바이스의 DIN에 연결한다
- DOUT은 CMOS 로직 레벨 출력으로 연속적으로 직렬 연결된 파트의 DIN을 손쉽게 구동한다
- 사용 예) 네 개의 MAX 7219가 직렬 연결되어있고 네 번째 칩에 데이터를 쓰기 위해 원하는 16비트 워드를 전송한 다음 3개의 비작동 코드를 전송한다
- LOAD/CS 가 HIGH가 될 때, 모든 디바이스의 데이터가 잠긴다.
- 처음 세 개의 칩은 No-Op 명령어를 수신하고 네번째 디바이스는 목표한 데이터를 수신한다
[CUBE MX 설정]
- STM32WBXX 의 경우, SPI1 보드 레이트는 APB2 Peripheral Clock을 기준으로 정해진다
- 디폴트 설정인 APB2 Peripheral clock 32Mhz, SPI1 Prescaler 4 의 값으로 SPI1 보드 레이트는 8.0Mbits/s
- MAX7219는 데이터를 수신하는 역할만 하므로 Transmit Only Master 선택
- NSS – CS 역할 (Master일 경우에만 Output 사용 가능, 사용 보드의 경우 NSS핀으로 PA4 자동 사용 설정)
- (Hardware NSS를 사용하지 않고 GPIO를 소프트웨어 컨트롤해 CS 라인을 제어해도 된다)
[코드]
– 정의
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define max7219_SPI hspi1 //MAX7219 통신에 사용되는 SPI를 사용하기 편하게 재정의 //HAL_SPI_Transmit_IT(&max7219_SPI, ...) = HAL_SPI_Transmit_IT(&hspi1, ...) (SPI1 사용) #define max7219_No_Op_Mode 0x00 //No-Op 모드 #define max7219_Decode_Mode 0x09 //디코드 모드 #define max7219_Intensity_Mode 0x0A //디스플레이 밝기 모드 #define max7219_Scan_Limit_Mode 0x0B //스캔 리미트 모드 #define max7219_Display_Test_Mode 0x0F //디스플레이 테스트 모드 #define max7219_Display_Test_Enter 1 //테스트 모드 진입 #define max7219_Display_Test_Exit 0 //테스트 모드 종료, 노멀 모드로 전환 #define max7219_Shutdown_Mode 0x0C //셧다운 모드 #define max7219_Shutdown_Enter 0 //셧다운 모드 진입 #define max7219_Shutdown_Exit 1 //셧다운 모드 종료, 노멀 모드로 전환 #define max7219_blank 0x0F //디코드 모드에서 디스플레이 공백 표현에 사용 /* USER CODE END PD */ |
– 명령 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* 명령어와 명령어에 따른 설정값을 받아와 전송하는 함수 */ static void max7219_cmd(int cmd, int data) //cmd=ADDRESS(명령어 역할), data=명령어 설정값 { uint16_t write_buffer=0; //MAX7219에 전송할 16비트 변수 write_buffer=(cmd<<8)|data; //전달받은 명령어는 상위 8비트, 명령어에 따른 설정값은 하위 8비트에 저장 HAL_SPI_Transmit_IT(&max7219_SPI,(uint8_t *)&write_buffer,1); //MAX7219에 전송 while(HAL_SPI_GetState(&max7219_SPI)==HAL_SPI_STATE_BUSY_TX) { //전송 완료까지 대기 } } |
- 16비트 포맷을 사용해야 하므로 MAX7219에 전송할 16비트 변수를 선언
- 명령어와 명령어에 따른 설정값을 받아와서 앞서 선언한 16비트 변수의 상위 8비트에는 명령어(ADDRESS)를 하위 8비트에는 데이터(명령어 설정값)을 입력한다
- MAX7219의 모든 기능이 동일한 데이터 수신 방법을 사용하므로 이 함수만으로도 모든 기능을 사용할 수 있다
– 숫자 입력 함수
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
/* 숫자를 입력할 자리와 입력할 데이터를 받아와 전송하는 함수 단순히 명령어를 입력하는 것과 디스플레이에 표현할 숫자를 입력하는 것을 구분 짓기 위해 만든 것으로 데이터 전달 방식에서 max7219_cmd() 함수와의 차이는 없다 */ static void max7219_write_value(int digit, int value, int decimal_point) //digit=숫자를 입력할 자리(1~8), value=입력할 데이터, 소수점 표현 (0=미사용, 1=사용) { uint16_t write_buffer=0; //MAX7219에 전달할 16비트 변수 선언 /* 디코드 모드를 사용할 때, (-, E, H, L, P, ' ')은 아스키 코드값으로 10이 넘는 인티저 값을 갖는 것을 이용 (value!=0x20) 은 디코드 미사용할 때, 세그먼트 라인 B를 표기하는데 문제가 생기는 것을 막기 위함 (' ' 과 동일한 값을 같기 때문에 사용자가 0x20을 입력하면 ' ' 취급되어 실제론 0x0F가 입력된다) */ if((20<value)&&(value!=0x20)) { char w_char=(char)value; switch(w_char) { case '-' : value=0x0A; //디코드 모드에서 '-' 를 표현하기 위한 레지스터 데이터 값 break; case 'e' : case 'E' : value=0x0B; break; case 'h' : case 'H' : value=0x0C; break; case 'l' : case 'L' : value=0x0D; break; case 'p' : case 'P' : value=0x0E; break; case ' ' : value=0x0F; break; default : break; } } write_buffer=((digit+1)<<8)|value; //디스플레이의 자릿수는 0x01부터 시작해 0x08까지 사용 if(decimal_point==1) { write_buffer|=0x80; } //소수점을 사용할 경우, 7번째 비트를 High(1)로 설정 HAL_SPI_Transmit_IT(&max7219_SPI,(uint8_t *)&write_buffer,1); while(HAL_SPI_GetState(&max7219_SPI)==HAL_SPI_STATE_BUSY_TX) { } } |
- 단순히 명령어 전달과 디스플레이 표현을 구분지어 사용하기 위해 선언한 함수
- 전달하는 데이터의 크기 및 데이터 구조는 명령 함수와 동일하다
- 소수점 표현의 경우, 7번 비트를 로직 High(1)로 설정해주면 된다
- ex ) 디스플레이 2번째 자리에 2. 을 표현하기 위해선 (디스플레이 첫번째 자리 = Digit 0 이라 가정)
- 상위 8비트 : 디스플레이 4번째 자리 Digit 3 = 0x04 (Digit0=0x01 부터 시작 (0x00 (X))
- 하위 8비트 : 숫자 2 = 0x02, 소수점 = 0x70 => 0x72
– 초기화 예시
1 2 3 4 5 6 7 8 9 10 11 |
int main(void) { ... max7219_cmd(max7219_Shutdown_Mode,max7219_Shutdown_Exit); //셧다운 모드 종료, 노멀 작동 시작 max7219_cmd(max7219_Scan_Limit_Mode,0x07); //스캔 리미트 모드, 0x07=디스플레이 8자리 모두 사용 max7219_cmd(max7219_Decode_Mode, 0xFF); //디코드 모드, 디스플레이 8자리 모두 디코드 모드로 설정 max7219_cmd(max7219_Intensity_Mode, 0x08); //디스플레이 밝기, 중간으로 설정 (0x00~0x0F 16단계) } |
- 작동이 시작 됐을 때 MAX7219는 셧다운 모드에 진입한다고 되있었으므로 셧다운 모드 종료를 먼저 실행
- 디스플레이를 사용하기 전에 프로그래밍을 하지 않을 경우, 첫째 자리를 스캔하도록 설정되고 데이터 레지스터의 데이터를 디코딩하지 않으며 밝기 레지스터는 최소값으로 설정되므로
- 스캔 리미트 모드에서 디스플레이 8자리 모두 사용 설정 (0x00~0x07)
- 디코드 모드에서 디스플레이 8자리 모두 디코드 모드 사용 설정 (데이터 비트=디스플레이 자릿수, 1=디코드 사용)
- 디스플레이 밝기 모드에서 16단계 중 9단계로 설정
– 단순 사용 예시
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
int main(void) { ... /* USER CODE BEGIN 2 */ max7219_cmd(max7219_Shutdown_Mode,max7219_Shutdown_Exit); max7219_cmd(max7219_Scan_Limit_Mode,0x07); max7219_cmd(max7219_Decode_Mode, 0x00); //디스플레이 모든 자리 디코드 미사용 max7219_cmd(max7219_Intensity_Mode, 0x08); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ max7219_cmd(max7219_Decode_Mode, 0); //모든 자리 디코드 미사용 for(uint8_t i=0;i<8;i++) //디스플레이 모든 자리 공백으로 초기화 { max7219_write_value(i,0,0); //디코드 모드 공백 : 0x0F, 디코드 미사용 공백 : 0x00 } for(int i=0;i<8;i++) { int k=0; for(int j=0;j<8;j++) { max7219_write_value(i,k|=(0x01)<<j,0) //비트0 부터 MSB로 1비트씩 비트 시프트 및 OR 연산으로 해당 자리의 모든 세그먼트 라인을 켬 HAL_Delay(100); } } max7219_cmd(max7219_Decode_Mode,0xFF); //모든 자리 디코드 사용 for(uint8_t i=0;i<8;i++) { max7219_write_value(i,max7219_blank,0); //디스플레이 모든 자리 공백으로 초기화 (DATA : 0x0F) } for(uint8_t i=0;i<8;i++) { max7219_write_value(i,'-',1); //디스플레이 -. (소수점 표기 사용)으로 채움 HAL_Delay(500); } for(uint8_t i=0;i<8;i++) { max7219_write_value(i,i,0); //디스플레이 자릿수와 일치하는 숫자로 채움 HAL_Delay(500); } max7219_write_value(7,'H',0); HAL_Delay(500); max7219_write_value(6,'E',0); HAL_Delay(500); max7219_write_value(5,'L',0); HAL_Delay(500); max7219_write_value(4,'P',0); HAL_Delay(500); } /* USER CODE END 3 */ } |
- 디스플레이 모든 자리 사용, 디코드 미사용, 밝기 9단계로 초기화
- 디스플레이 모든 자리를 공백으로 초기화 (디코드 미사용일 때 공백을 표현하기 위한 데이터값은 0x00)
- 디스플레이 0번부터 7번까지 순차적으로 세그먼트 라인을 하나씩 채워나감
- 디스플레이 모든 자리를 디코드 사용으로 바꾼 뒤 공백으로 초기화 (디코드 모드 공백 데이터 : 0x0F)
- (디코드 사용/미사용 전환을 해도 각 자리의 레지스터 데이터는 유지된 상태로 존재하기 때문에 디코드 사용/미사용에 따라 디스플레이 표현만 변한다)
- 디스플레이 0번부터 7번까지 순차적으로 ‘-.’ 문자를 표시
- 디스플레이 자리와 동일한 숫자값을 디스플레이 0번부터 7번까지 순차적으로 표현
- 디스플레이 7번부터 4번까지 순차적으로 H, E, L, P 문자 데이터 입력
- 디스플레이 자리는 0x01(=0번째)부터 시작하므로 상위 8비트 0x06은 Digit 5(다섯번째 자리)를 ADDRESS로 사용했음을 의미한다
- 하위 8비트는 0x05이므로 디스플레이 다섯번째 자리에 숫자 5가 표현된다