![[ARM] STM32_LCD I2C, ADC(polling 방식, DMA 방식), CDS](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnLTTm%2FbtsGefoK3HR%2FnctILkQRxBL7os5ffvpcMk%2Fimg.jpg)
1. I2C 통신
Write : [장치 주소(7bit) + 0(Write)] [쓰기 데이터(8bit)]
Read : [장치 주소(7bit) + 1(Read)] [읽기 데이터(8bit)]
⦁ 모든 data 뒤에는 ACK bit가 붙음(쓰기에서는 slave가, 읽기에서는 master가 보냄)
2. LCD I2C
∘ 16x2 LCD
2-1. LCD 메모리
1. CGROM(Character Generator ROM)
⦁ 8비트 문자 패턴을 저장하고 있는 메모리
⦁ 208개의 5x8 도트와 32개의 5x10 도트의 문자 패턴 저장(9920bit)
⦁ Character Code 0b0010 0000(공백) ~ 0b0111 1111(←)까지는 ASCII (아스키 코드) 와 일치
→ c언어에서 문자로 표현된 데이터를 변환하지 않고 그대로 LCD에 출력 가능
→ 위 표와 같이 문자가 CGROM에 저장되어 있고 저장된 문자를 DDRAM을 통해 화면에 출력
→ 표 좌측 CGRAM(1) ~ CGRAM(8)은 사용자가 문자를 만들어 CGRAM에 저장하여 사용 가능
2. CGRAM(Character Generator RAM)
⦁ 사용자 정의 문자를 저장하는 메모리
⦁ 6bit Address
⦁ 8개의 5×8 도트 혹은 4개의 5×10 도트의 문자 패턴 저장 가능(1개 문자당 8byte이기때문에 메모리 크기 : 64byte)
⦁ Character Code 0b0000 0000 ~ 0b0000 1111일 때 CGRAM에 저장된 사용자 정의 문자를 출력 가능
→ 위 표와 같이 CGRAM Address 0b00 0000 ~ 0b00 0111 8개의 주소(LCD블록 하나의 세로 길이 = 8)가 하나의 Charatcer Code
3. DDRAM(Display Data RAM)
⦁ LCD 화면에 표시되는 실제 데이터가 저장되는 메모리
⦁ 80개의 문자 저장(1x80byte)
⦁ 1행 주소 : 0x00 ~ 0x27, 2행 주소 : 0x40 ~ 0x67
→ 위 표와 같이 DDRAM에 저장할 수 있는 문자는 80개이지만 LCD Display가 16x2이기 때문에 32개의 문자만 출력
(나머지 문자는 저장되어 있지만 보이지 않는 것이기 때문에 display shift하면 볼 수 있다.)
▶ LCD 모듈은 Character Code를 통해 메모리에 저장된 문자를 LCD에 표시한다.
2-2. LCD Register
1. IR(Instruction Register)
⦁ LCD 모듈을 제어하기 위한 명령을 보관하고 있는 레지스터
⦁ 제어에 필요한 명령 저장 및 DDRAM 위치 주소와 CGRAM 위치 주소 정보 저장
2. DR(Data Register)
⦁ DDRAM, CGRAM에 write/read할 데이터를 임시로 저장
RS : 레지스터 선택
RW : Read/Write
2-3. 기타
1. AC(Address Counter)
⦁ DDRAM, CGRAM 주소 할당
⦁ 명령어의 주소가 IR에 저장 → 주소 정보가 IR에서 AC로 전송 → 메모리 write/read → AC(메모리 주소) 1 증가/감소
⦁ RS = 0, R/W = 1일 때 AC 내용이 DB0~DB6으로 출력
⇒ LCD에 문자열을 출력할 때 DDRAM 주소를 하나씩 증가시키지 않아도 한글자 출력 후 AC에서 주소를 +1 하기 때문에 첫글자를 출력할 위치(DDRAM Address)만 지정하면 그 다음은 자동으로 다음 위치(Address)에 출력됨
2. BF(Busy Flag)
⦁ BF = 1이면, 모듈은 내부 동작 모드 → 다음 명령 불가
⦁ RS = 0, R/W = 1이면, BF 데이터가 DB7로 출력
2-4. LCD I2C 드라이버 생성
I2C_CLCD.c 코드
#include "I2C_CLCD.h"
extern I2C_HandleTypeDef hi2c1;
void I2C_CLCD_Delay_us(uint8_t us)
{
volatile uint8_t i;
for(i = 0; i < 19*us/10; i++);
}
void I2C_CLCD_SendByte(uint8_t RS_State, uint8_t Byte)
{
uint8_t i;
uint8_t buffer[4];
// 1바이트 data를 4bit씩 쪼갬
for(i = 0; i < 2; i++)
{
buffer[i] = (Byte & 0xf0) | // P4~P7
(1 << I2C_CLCD_LED) | // P3
(!i << I2C_CLCD_E) | // P2 Enable 신호를 1, 0의 순서로 전송
(0 << I2C_CLCD_RW) | // P1 (Read = busy check)
(RS_State << I2C_CLCD_RS); // P0
// 상위 4개 비트 : data(P4 ~ P7), 하위 4개 비트 : LED,E,RW,RS(P3, P2, P1, P0)
// I2C 모듈에서 LCD에 해당 핀에 해당 비트를 전송
}
for(i = 0; i < 2; i++)
{
buffer[i+2] = (Byte << 4) |
(1 << I2C_CLCD_LED) |
(!i << I2C_CLCD_E) |
(0 << I2C_CLCD_RW) |
(RS_State << I2C_CLCD_RS);
// 상위 4개 비트 : data, 하위 4개 비트 : LED,E,RW,RS
}
HAL_I2C_Master_Transmit(&hi2c1, PCF8574_AD | WRITE, buffer, 4, 300);
I2C_CLCD_Delay_us(40);
}
void I2C_CLCD_init(void)
{
uint8_t i;
uint8_t CLCD_Init_CMD[4] = {0x28, // Function set
0x0c, // Display ON
0x01, // Clear Display
0x06}; // Entry mode set
HAL_Delay(100);
I2C_CLCD_SendByte(0, 0x02); // Return home
HAL_Delay(2);
for(i = 0; i < 4; i++)
{
I2C_CLCD_SendByte(0, CLCD_Init_CMD[i]);
if(i == 2) HAL_Delay(2);
}
}
void I2C_CLCD_GotoXY(uint8_t X, uint8_t Y)
{
I2C_CLCD_SendByte(0, 0x80 | (0x40 * Y + X)); // set DDRAM
}
void I2C_CLCD_PutC(uint8_t C)
{
if(C == '\f')
{
I2C_CLCD_SendByte(0, 0x01); // write COMMAND (Clear display)
HAL_Delay(2);
}
else if(C == '\n')
{
I2C_CLCD_GotoXY(0, 1);
}
else
{
I2C_CLCD_SendByte(1, C); // write DATA
}
}
void I2C_CLCD_PutStr(uint8_t *Str)
{
while(*Str) I2C_CLCD_PutC(*Str++);
// 문자열의 끝(값이 없을 때)까지 문자 하나씩 출력
}
코드 리뷰
void I2C_CLCD_SendByte(uint8_t RS_State, uint8_t Byte)
{
uint8_t i;
uint8_t buffer[4];
// 1바이트 data를 4bit씩 쪼갬
for(i = 0; i < 2; i++)
{
buffer[i] = (Byte & 0xf0) | // P4~P7
(1 << I2C_CLCD_LED) | // P3
(!i << I2C_CLCD_E) | // P2 Enable 신호를 1, 0의 순서로 전송
(0 << I2C_CLCD_RW) | // P1 (Read = busy check)
(RS_State << I2C_CLCD_RS); // P0
// 상위 4개 비트 : data(P4 ~ P7), 하위 4개 비트 : LED,E,RW,RS(P3, P2, P1, P0)
// I2C 모듈에서 LCD에 해당 핀에 해당 비트를 전송
}
for(i = 0; i < 2; i++)
{
buffer[i+2] = (Byte << 4) |
(1 << I2C_CLCD_LED) |
(!i << I2C_CLCD_E) |
(0 << I2C_CLCD_RW) |
(RS_State << I2C_CLCD_RS);
// 상위 4개 비트 : data, 하위 4개 비트 : LED,E,RW,RS
}
HAL_I2C_Master_Transmit(&hi2c1, PCF8574_AD | WRITE, buffer, 4, 300);
I2C_CLCD_Delay_us(40);
}
왼쪽 회로도와 같이 I2C 모듈과 LCD 모듈 연결
P0 ~ P3 : RS, RW, E, A(backlight)
P4 ~ P7 : D4 ~ D7
⦁ data = 0bxxxx xxxx(P7 ~ P0)이기 때문에 출력 데이터는 상위 비트로, 제어 데이터는 하위비트로 저장한다.
⦁ LCD Data pin 4개만 I2C 모듈과 연결되어 있기 때문에 8bit 통신이 아니라 4bit 통신을 해야한다.
→ 8bit 출력 데이터를 4bit로 쪼개어 두번 전송
⦁ Timing Diagram과 같이 Enable pin이 High → LOW 되는 순간 저장된 Data가 출력
⦁ 1Byte Data를 4bit씩 쪼개고 쪼갠 4bit를 Enable High 한번, Low 한번 총 두번 전송
→ 1byte Data를 전송하기 위해서는 총 4번의 전송 절차가 필요
2-5. I2C 주소 확인
∘ I2C 주소 범위인 0 ~ 256을 하나씩 확인
∘ `HAL_I2C_IsDeviceReady()` 함수 이용
→ 매개변수에 주소값을 넣으면 해당 주소의 I2C 장치가 정상인지 아닌지 확인 할 수 있음
main.c 코드
/* USER CODE BEGIN 2 */
initUart(&huart2);
// 8bit address check one by one(address 7bit + read/write 1bit)
for(int address = 0; address < 256; address++)
{
if(HAL_I2C_IsDeviceReady(&hi2c1, address, 0, 10) == HAL_OK)
/*
* address : I2C 주소
* 0 : Trials(준비될때까지 시도할 최대 회수)
* 10 : 시도 제한시간(ms)
*/
{
printf("%02x is ready\n", address);
}
}
printf("I2C scan end\n");
/* USER CODE END 2 */
4E : 0b0100 1110
4F : 0b0100 1111
LSB : R/W bit
실제 주소 : R/W bit 뺀 값 = 27(0b0010 0111)
2-6. Display shift
∘ datasheet에 따라 Display shift COMMAND 전송
(1) Display right shift COMMAND : 0b 00 0001 1100
(2) Display left sifht COMMAND : 0b 00 0001 1000
(3) Display shift stop COMMAND : 0b 00 0001 0000
I2C_CLCD.c 코드
void I2C_CLCD_Cursor(uint8_t on)
{
I2C_CLCD_SendByte(0,0x0c|(on<<1));
// on = 0) cursor off
// on = 1) cursor on
}
void I2C_CLCD_Right()
{
I2C_CLCD_SendByte(0, 0b00011100);
}
void I2C_CLCD_Left()
{
I2C_CLCD_SendByte(0, 0b00011000);
}
void I2C_CLCD_ShiftStop()
{
I2C_CLCD_SendByte(0, 0b00010000);
}
LCD 드라이버에 위 코드 추가
main.c 코드
/* USER CODE BEGIN 2 */
initUart(&huart2);
I2C_CLCD_init();
I2C_CLCD_GotoXY(0, 0);
I2C_CLCD_PutStr(" ");
I2C_CLCD_GotoXY(0, 1);
I2C_CLCD_PutStr(" ");
I2C_CLCD_GotoXY(0, 0);
I2C_CLCD_PutStr("Hello world");
for(int i = 0; i < 12; i++)
{
I2C_CLCD_Left();
HAL_Delay(100);
}
I2C_CLCD_ShiftStop();
I2C_CLCD_Cursor(1);
/* USER CODE END 2 */
→ Hello world 라고 적힌 LCD 화면이 shift 되는 것을 확인 할 수 있다.
코드 리뷰
void I2C_CLCD_Right()
{
I2C_CLCD_SendByte(0, 0b00011100);
}
- Display right shift COMMAND는 `0b 00 0001 1100` 인데 `0b 0001 1100`만 전송하는 이유는?
void I2C_CLCD_SendByte(uint8_t RS_State, uint8_t Byte)
{
uint8_t i;
uint8_t buffer[4];
// 1바이트 data를 4bit씩 쪼갬
for(i = 0; i < 2; i++)
{
buffer[i] = (Byte & 0xf0) | // P4~P7
(1 << I2C_CLCD_LED) | // P3
(!i << I2C_CLCD_E) | // P2 Enable 신호를 1, 0의 순서로 전송
(0 << I2C_CLCD_RW) | // P1 (Read = busy check)
(RS_State << I2C_CLCD_RS); // P0
// 상위 4개 비트 : data(P4 ~ P7), 하위 4개 비트 : LED,E,RW,RS(P3, P2, P1, P0)
// I2C 모듈에서 LCD에 해당 핀에 해당 비트를 전송
}
- `0b 00 0001 1100`에서 상위 2비트는 RS, RW bit이다.(COMMAND모드이기에 RS:0, WRITE모드이기에 RW:0)
- `I2C_CLCD_SendByte()` 함수에서 RS와 RW bit를 따로 전송하기 때문에 RS, RW bit를 제외하고 DATA bit 8개만 매개변수로 넣어주면 된다.
2-7. Custom Font
- Custom Font 저장 및 출력하는 과정
- CGRAM Address 설정(접근한다고 이해하면 됨) ← RS=0 (COMMAND mode)
- 해당 CGRAM Address에 Custom Font 데이터 저장 ← RS=1 (DATA mode)
- DDRAM Address 설정(LCD 출력할 위치) ← RS=0
- 저장한 Custom Font의 Character Code를 해당 DDRAM Address에 저장 ← RS=1
- 원하는 위치에 Custom한 문자가 출력됨
⦁ CGRAM 주소 하나에 5bit의 데이터를 저장하여 사용자만의 Custom Font를 만들 수 있다.
(주소 8개가 하나의 문자(Charater Code)가 됨)
⦁ Character Code 4번째 bit는 0,1 상관 X
(Charater Code 0x00과 0x08은 똑같이 CGRAM의 첫번째 데이터를 가르킴)
⦁ Character Code 0b0000 *000 ~ 0b0000 *111까지 custom 가능하며 8개의 문자를 저장할 수 있다.
CLCD.c 코드
void I2C_CLCD_CustomFont()
{
I2C_CLCD_SendByte(0, 0x40);
// > (꺽새)출력
I2C_CLCD_SendByte(1, 0b10000);
I2C_CLCD_SendByte(1, 0b01000);
I2C_CLCD_SendByte(1, 0b00100);
I2C_CLCD_SendByte(1, 0b00010);
I2C_CLCD_SendByte(1, 0b00001);
I2C_CLCD_SendByte(1, 0b00010);
I2C_CLCD_SendByte(1, 0b00100);
I2C_CLCD_SendByte(1, 0b01000);
// 바탕있는 꺽새 출력
I2C_CLCD_SendByte(1, 0b01111);
I2C_CLCD_SendByte(1, 0b10111);
I2C_CLCD_SendByte(1, 0b11011);
I2C_CLCD_SendByte(1, 0b11101);
I2C_CLCD_SendByte(1, 0b11110);
I2C_CLCD_SendByte(1, 0b11101);
I2C_CLCD_SendByte(1, 0b11011);
I2C_CLCD_SendByte(1, 0b10111);
}
main.c 코드
I2C_CLCD_CustomFont();
I2C_CLCD_GotoXY(12, 0);
I2C_CLCD_PutC(0);
I2C_CLCD_PutC(1);
코드 리뷰
I2C_CLCD_SendByte(0, 0x40);
// > (꺽새)출력
I2C_CLCD_SendByte(1, 0b10000);
I2C_CLCD_SendByte(1, 0b01000);
I2C_CLCD_SendByte(1, 0b00100);
...
I2C_CLCD_CustomFont();
I2C_CLCD_GotoXY(12, 0);
I2C_CLCD_PutC(0);
I2C_CLCD_PutC(1);
(1) ` I2C_CLCD_SendByte(0, 0x40)`에서 CGRAM 첫번째 주소에 접근(RS=0)
(2) Custom 문자 저장(RS=1) ← AC에 의해 자동으로 주소 +1
(3) main.c의 `I2C_CLCD_GotoXY(12, 0)`에서 DDRAM 주소 설정(원하는 위치 설정, RS=0)
(4) ` I2C_CLCD_PutC(0)`에서 저장한 문자의 Character Code인 0을 입력하여 문자 출력(RS=1)
2-8. Progress Loading Bar
∘ 비어있는 문자 블록부터 차있는 문자 블록 Custom Font 생성(블록 하나의 가로 길이가 5이므로 총 5개)
∘ 점점 Loading Bar가 차오르는 것을 구현할 함수 생성
CLCD.c 코드
// custom font
void I2C_CLCD_CustomFont()
{
I2C_CLCD_SendByte(0, 0x40); // set CGRAM address(0b0100 0000)
for(int i = 0; i < 8; i++)
I2C_CLCD_SendByte(1, 0b00000); // PutC(0)
for(int i = 0; i < 8; i++)
I2C_CLCD_SendByte(1, 0b10000); // PutC(1)
for(int i = 0; i < 8; i++)
I2C_CLCD_SendByte(1, 0b11000); // PutC(2)
for(int i = 0; i < 8; i++)
I2C_CLCD_SendByte(1, 0b11100); // PutC(3)
for(int i = 0; i < 8; i++)
I2C_CLCD_SendByte(1, 0b11110); // PutC(4)
for(int i = 0; i < 8; i++)
I2C_CLCD_SendByte(1, 0b11111); // PutC(5)
}
// progress loading bar
void I2C_CLCD_Progressbar(uint8_t n, uint8_t line)
{
I2C_CLCD_GotoXY(0, line);
// 이전 차있는 블럭 배치
for(int i = 0; i < (n/5); i++)
{
I2C_CLCD_PutC(5);
}
// 현재 차는 블럭 배치
I2C_CLCD_PutC(n % 5); // parameter : 0~4
// 뒷쪽 비어있는 블럭 배치
for(int i = (n/5) + 1; i < 16; i++)
{
I2C_CLCD_PutC(0);
}
}
main.c 코드
int main(void) {
initUart(&huart2);
I2C_CLCD_init();
I2C_CLCD_GotoXY(0, 0);
I2C_CLCD_PutStr(" ");
I2C_CLCD_GotoXY(0, 1);
I2C_CLCD_PutStr(" ");
I2C_CLCD_CustomFont();
while (1) {
static int count = 0;
I2C_CLCD_Progressbar(count);
count++;
count %= 81; // x-axis(horizontal) length = 80
HAL_Delay(50);
}
}
코드 리뷰
LCD 한 블록당 가로축 길이(도트 개수) → 5
LCD 총 가로축 길이(도트 개수) → 16*5=80
이를 이용하여 Custom Font을 만들고 50ms마다 블록이 차오르는 것을 구현
3. ADC 2개 사용하여 LCD 제어
- Polling 방식 : 변환 대기
- Interrupt 방식 : 완료 시 인터럽트
- DMA 방식 : 자동 저장
3-1. ADC Polling 방식
- Polling : 데이터 전송 방식 중 하나
- ADC 수행하고 MCU에서 데이터를 읽어들이는 방식
- 단점
- MCU가 데이터 전송을 직접 처리하는 방식으로, 데이터가 준비될 때까지 대기하고 있는 동안에는 다른 작업 수행 불가
- MCU가 계속해서 데이터를 확인하고 대기하므로 시스템의 부하, 전력 소비 증가
- 동작 코드
- 변환 시작 `HAL_ADC_Start()` → 변환 완료 확인 `HAL_ADC_PollForConversion()` → 변환 값 읽기 `HAL_ADC_GetValue()` → 변환 종료 `HAL_ADC_Stop()`
- Scan Conversion : Enable(ADC 2개 이상일 떄)
- Continuius Conversion : Enable
- Number of Conversion : 2
- Rank 1 Channel : Channel 0 (Rank : channel 동작 순서)
- Rank 2 Channel : Channel 1
- Sampling Time : 56 Cycles → 56클럭동안 ADC값을 가지고 있다가 56클럭이 지나면 다시 변환
⦁ Analog 신호(가변 저항 조절)를 Digital 신호로 변환하여 progress bar 조절
- Analog 신호를 각기 다른 두개의 ADC를 이용하여 변환
main.c 코드
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "UART.h"
#include "I2C_CLCD.h"
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
I2C_HandleTypeDef hi2c1;
UART_HandleTypeDef huart2;
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP */
int main(void)
{
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART2_UART_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
initUart(&huart2);
I2C_CLCD_init();
I2C_CLCD_GotoXY(0, 0);
I2C_CLCD_PutStr(" ");
I2C_CLCD_GotoXY(0, 1);
I2C_CLCD_PutStr(" ");
I2C_CLCD_GotoXY(0, 0);
I2C_CLCD_CustomFont();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// ADC range : 0~4095(12bit)
// PollForConversion 호출하면 channel 0부터 시작 -> 다시 호출하면 channel 1 ... 반복
// A0 변환
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 10);
uint16_t adcValue1 = HAL_ADC_GetValue(&hadc1);
// A1 변환
HAL_ADC_PollForConversion(&hadc1, 10);
uint16_t adcValue2 = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
// LCD display
I2C_CLCD_Progressbar(adcValue1/51, 0); // 4095 / 80(LCD 가로 길이) = 51
I2C_CLCD_Progressbar(adcValue2/51, 1);
/*
* MAX first parameter = 16(LCD lenght)*5(LCD block lenght) = 80
* range of first parameter = 0 ~ 80
* range of adcValue = 0 ~ 4095(12bit)
*
* First parameter formula = adcValue / x
* x = adcValue / first parameter = 4095 / 80 = 51
* -> first parameter = adcValue / 51 (Conversion the range of adcValue)
*/
HAL_Delay(50);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
3-2. ADC DMA 방식
- DMA(Direct Memory Access) : 데이터 전송 방식 중 하나
- MCU의 개입 없이 주변 장치와 메모리 간 데이터 전송
- 장점
- MCU는 DMA가 데이터 전송 처리하는 동안에 다른 작업 수행 가능
- 전력 소비가 감소, 데이터 전송 빠르고 효율적
- 동작 방식
- 배열 선언 `uint16_t adcValue[2]` → DMA 모드 시작 `HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adcValue,2)`
- 배열에 ADC 결과가 배열에 저장됨, CPU가 ADC 결과가 나오기까지 기다리지 않아도 됨
main.c 코드
int main(void)
{
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C1_Init();
MX_USART2_UART_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
initUart(&huart2);
I2C_CLCD_init();
I2C_CLCD_GotoXY(0, 0);
I2C_CLCD_PutStr(" ");
I2C_CLCD_GotoXY(0, 1);
I2C_CLCD_PutStr(" ");
I2C_CLCD_CustomFont();
//setup ADC
uint16_t adcValue[2];
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adcValue,2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
I2C_CLCD_Progressbar(adcValue[0]/51,0);
I2C_CLCD_Progressbar(adcValue[1]/51,1); //4095(ADC 12bit)/80(LCD 길이(5*16)) = 51
HAL_Delay(50);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
- 동작 결과는 Polling 방식과 같음
4. CDS(조도 센서)
빛의 양을 측정하는 광센서
: 빛에 따라 저항 값이 변하며 빛을 받으면 내부 저항 값이 감소하며 빛이 없으면 내부 저항 값이 급격히 증가하는 특성
- CDS의 저항값이 변하는 것을 읽고 ADC변환하여 그래프로 확인
*I2C_CLCD.c*
/*CGBuffer[0],CGBuffer[1],CGBuffer[2],CGBuffer[3],
CGBuffer[4],CGBuffer[5],CGBuffer[6],CGBuffer[7],
0000 0000 0000 [0000 0][000 00][00 000][0 0000] =20 bit
12bit는 버림 0 1 2 3
*/
uint32_t CGBuffer[16];
void I2C_CLCD_CG_ClearBuffer()
{
memset(CGBuffer,0,64);
}
void I2C_CLCD_CG_ScrollLeft()
{
for(int i = 0; i <16; i++){
CGBuffer[i] = CGBuffer[i]<<1;
}
}
// save (x,y) value
void I2C_CLCD_CG_DrawPixel(uint8_t x, uint8_t y)
{ //20번째(x)에 조도값(y)에 점을 찍음
CGBuffer[y] |= 1 << (19-x);
/*
* CGBuf[0] = 0000 0000 0000 1111 1111 1111 1111 1111
* 버퍼의 매개변수 = y축 (0~15)
* 버퍼에 저장된 값 = x축 (x축 범위가 0~19이므로 0번째 bit ~ 19번째 bit까지만 사용)
*/
}
// CGRAM value update
void I2C_CLCD_CG_Update()
{
I2C_CLCD_SendByte(0,0x40);
for(int j = 0; j < 4;j++)
{ //CGRAM0 ~ CGRAM3(LCD char memory)
for(int i =0; i < 8;i++)
{ // 8줄을 한 줄씩 CGRAM 저장
I2C_CLCD_SendByte(1,CGBuffer[i]>>(15-j*5));
// LCD 블록 한칸 가로 길이가 5이므로 5bit씩 CGRAM 저장
// CGRAM에 저장되는 값은 0bxxx11111 으로 상위 3bit는 안쓰이고 하위 5bit만 유효한 값임
}
}
for(int j = 0; j < 4;j++)
{ //CGRAM4 ~ CGRAM7
for(int i =0; i < 8;i++)
{
I2C_CLCD_SendByte(1,CGBuffer[i+8]>>(15-j*5));
}
}
}
*main.c*
int main(void)
{
initUart(&huart2);
I2C_CLCD_init();
I2C_CLCD_GotoXY(0, 0);
I2C_CLCD_PutStr(" ");
I2C_CLCD_GotoXY(0, 1);
I2C_CLCD_PutStr(" ");
I2C_CLCD_CustomFont();
// setup ADC
uint16_t adcValue[2];
// Save the ADC result value using DMA
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adcValue,2);
// LCD 자리 정함 (LCD 첫줄은 CGRAM 0 ~ 3, 두번째 줄은 CGRAM 4 ~ 7 출력)
I2C_CLCD_GotoXY(0,0);
I2C_CLCD_PutC(0);
I2C_CLCD_PutC(1);
I2C_CLCD_PutC(2);
I2C_CLCD_PutC(3);
I2C_CLCD_GotoXY(0,1);
I2C_CLCD_PutC(4);
I2C_CLCD_PutC(5);
I2C_CLCD_PutC(6);
I2C_CLCD_PutC(7);
I2C_CLCD_CG_Update();
while (1) {
I2C_CLCD_CG_ScrollLeft();
// 19: x축 길이(0~19) / 15:y축 길이(0~15)
I2C_CLCD_CG_DrawPixel(19, 15-(adcValue[0]/273)); //273 = 4095/15
/*
* x축 맨 오른쪽에 있는 값만 계속 바뀜 -> ScrollLeft() 함수에서 LSB를 left shift 시킴
*/
I2C_CLCD_CG_Update();
I2C_CLCD_GotoXY(5,0);
/*char str[20];
sprintf(str,"%5d",adcValue[0]);
I2C_CLCD_PutStr(str);
HAL_Delay(50);
HAL_Delay(50);*/
}
/* USER CODE END 3 */
}
Made By Minseok KIM
'ARM > 1_Study' 카테고리의 다른 글
[ARM] STM32_FND,DHT11 (0) | 2024.04.08 |
---|---|
[ARM] STM32_RTC, Switch (0) | 2024.04.02 |
[ARM] STM32_Radar (0) | 2024.03.24 |
[ARM] STM32_Buzzer, Ulatrasonic, 필터링, ServoMotor (0) | 2024.03.19 |
[ARM] STM32_TIMER정리, data 전송 flow 구상, PWM(LED, Buzzer) (0) | 2024.03.18 |
Let's Be Happy!
도움이 되었으면 좋겠어요 :)