앞서 I2C를 이용한 인터페이스에서 온습도 센서를 다루었다. 임베디드 보드나 필자가 주로 다녔던 업체에서 앞서 해봤던 SHT시리즈의 온습도센서나 EEPROM은 사실상 거의 필수로 들어가던 파츠였으며, 어느 회사를 가던 주로 많이 사용하는 것들이다. 소스를 잠깐 보고가는게 좋겠다 싶어서 해당 내용을 다룬다.
--- contents ---
01. [ESP32] VS Code 개발환경 구성 : https://makeutil.tistory.com/303
02. [ESP32] 첫 프로젝트 생성하기 : https://makeutil.tistory.com/304
03. [ESP32] 멀티 테스크 예제 (2 Task) : https://makeutil.tistory.com/305
04. [ESP32] Task간 데이터 공유 (Queue, Mutex) : https://makeutil.tistory.com/306
05. [ESP32] 개발보드 별 형상 및 I/O (ESP32/ESP32-S3) : https://makeutil.tistory.com/307
06. [ESP32] UART 통신 예제 #1 : https://makeutil.tistory.com/309
07. [ESP32] UART 통신 예제 #2 : https://makeutil.tistory.com/313
08. [ESP32] GPIO LED 켜기 : https://makeutil.tistory.com/311
09. [ESP32] Timer와 PWM을 이용한 LED 점멸 : https://makeutil.tistory.com/312
10. [ESP32] WiFi SoftAP와 WiFi Station (기본예제) : https://makeutil.tistory.com/314
11. [ESP32] 무선 시리얼 통신장치 만들기 1 (SoftAP) : https://makeutil.tistory.com/315
12. [ESP32] 무선 시리얼 통신장치 만들기 2 (SoftAP STA) : https://makeutil.tistory.com/316
13. [ESP32] I2C Temperature Sensor (DHT21) : https://makeutil.tistory.com/317
14. [ESP32] ESP32-S3 구매 할 때 고려할 점 : https://makeutil.tistory.com/318
15. [ESP32] Web Server - HTML, Javascript : https://makeutil.tistory.com/320
16. [ESP32] Web Server - GPIO Control (LED 제어) : https://makeutil.tistory.com/321
17. [ESP32] I2C EEPROM (AT24C0x) : 현재글
A1. [ESP32] 오류 - Fatal Error : No such file or directory : https://makeutil.tistory.com/308
------------------
EEPROM은 Electrically Erasable Programmable Read Only Memory의 약자로, 일반적인 ROM(Mask Read Only Memory) 와 기록된 데이터를 전자적으로 지울수 있는 ROM이다. 이러한 ROM은 AP(Application Processor)나 MCU등에 연결되어, 장치의 기본설정 데이터, 맥어드레스등 여러가지 데이터를 저장하는 목적으로 사용된다.
사실 임베디드 시스템을 처음 배우는 사람들에게 SHT 온습도센서나 EEPROM은 데이터시트를 보기에 아주 좋은 예를 제공한다. 그래서 해당 내용도 담고 싶긴하지만 아쉽게도, 필자에게 그런 시간적인 여유가 없다. 소스 위주로 한번 보고 필요한 내용만을 살짝 언급하고 넘어가도록 하겠다.
I2C와 관련된 내용은 이전의 SHT(DHT)를 참고하도록 하자.
1. 연결
I2C이므로 연결도 동일하다. 필자가 계속해서 19번과 20번을 사용하는 이유는 어느정도 익숙해지라고 하는 것이며, 독자 여러분들 께서는 다른핀을 사용해도 된다. 절대로 귀찮아서가 아니다. 절대로...
2. EEPROM 소스
EEPROM 소스를 확인해보도록 하자.
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "driver/i2c.h"
#include "driver/i2c_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "i2c_eeprom.h"
#include "esp_log.h"
#include "esp_err.h"
#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_SCL_IO20 20
#define I2C_MASTER_SDA_IO19 19
#define I2C_MASTER_FREQ_HZ 100000
#define I2C_MASTER_TIMEOUT_MS 1000
// EEPROM AT24C0x 시리즈의 기본 주소는 0x50(A0~2 모두 GND와 연결)이다.
#define AT24C04_ADDR 0x50
// 페이지 사이즈가 16 (한번에 기록할 수 있는 최대 바이트)
#define EEPROM_PAGE_SIZE 16
static const char *TAG = "AT24C04";
esp_err_t i2c_master_init(void)
{
// I2C 마스터로 설정, SDA와 SCL은 19-20번으로 설정 시스템 풀업 사용, 일반속도(100Khz)
i2c_config_t conf =
{
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO19,
.scl_io_num = I2C_MASTER_SCL_IO20,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ
};
// 파라메터 설정후 드라이버 등록
ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_NUM, &conf));
return i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);
}
// EEPROM에 데이터를 기록
esp_err_t at24c04_write(uint8_t mem_addr, const uint8_t *data, size_t len)
{
if (len > EEPROM_PAGE_SIZE) return ESP_ERR_INVALID_ARG;
i2c_cmd_handle_t cmd = i2c_cmd_link_create(); //I2C 연결 핸들 생성
i2c_master_start(cmd); // I2C 통신 시작조건 전송
// EEPROM(0x50)을 쓰기모드로 설정
i2c_master_write_byte(cmd, (AT24C04_ADDR << 1) | I2C_MASTER_WRITE, true);
// EEPROM 기록을 위한 주소 설정
i2c_master_write_byte(cmd, mem_addr, true);
// 데이터를 기록
i2c_master_write(cmd, data, len, true);
// 통신 종료 조건 전송
i2c_master_stop(cmd);
// 앞서 설정된 명령을 물리적인 I2C라인에 전송
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
// 연결한 메모리 삭제 및 기록완료 대기 10ms
i2c_cmd_link_delete(cmd);
vTaskDelay(pdMS_TO_TICKS(10));
return ret;
}
// EEPROM에서 데이터 읽기
esp_err_t at24c04_read(uint8_t mem_addr, uint8_t *data, size_t len)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
// 쓰기모드로 특정 주소 설정
i2c_master_write_byte(cmd, (AT24C04_ADDR << 1) | I2C_MASTER_WRITE, true);
// 읽을 메모리 위치 설정
i2c_master_write_byte(cmd, mem_addr, true);
i2c_master_start(cmd);
// 읽기 모드로 변경
i2c_master_write_byte(cmd, (AT24C04_ADDR << 1) | I2C_MASTER_READ, true);
지정된 주소에서 데이터를 읽어오고, NACK을 보냄.
i2c_master_read(cmd, data, len, I2C_MASTER_LAST_NACK);
i2c_master_stop(cmd);
// 설정된 명령을 실제 물리 라인에 적용
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
void app_main(void)
{
ESP_ERROR_CHECK(i2c_master_init());
const char *text = "ESP32,EEPROM,HelloWorld";
size_t text_len = strlen(text);
uint8_t mem_addr = 0x00;
ESP_LOGI(TAG, "Writing to EEPROM...");
// 16바이트씩 나눠서 쓰기
for (int i = 0; i < text_len; i += EEPROM_PAGE_SIZE) {
// 남은 데이터 크기확인
size_t data_len = (text_len - i > EEPROM_PAGE_SIZE) ?
EEPROM_PAGE_SIZE : (text_len - i);
esp_err_t ret = at24c04_write(mem_addr + i, (const uint8_t *)(text + i), data_len);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Write failed at addr 0x%02X", mem_addr + i);
return;
}
vTaskDelay(pdMS_TO_TICKS(10)); // EEPROM 쓰기 딜레이
}
// 읽기 버퍼 초기화
uint8_t read_buf[64];
memset(read_buf, 0, sizeof(read_buf));
ESP_LOGI(TAG, "Reading from EEPROM...");
// 16바이트 씩 나눠서 읽음.
for (int i = 0; i < text_len; i += EEPROM_PAGE_SIZE) {
// 남은 데이터 확인
size_t data_len = (text_len - i > EEPROM_PAGE_SIZE) ?
EEPROM_PAGE_SIZE : (text_len - i);
// 메모리에서 버퍼로 계산된 크기만큼 읽어옴.
esp_err_t ret = at24c04_read(mem_addr + i, read_buf + i, data_len);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Read failed at addr 0x%02X", mem_addr + i);
return;
}
}
// 읽기가 완료되면 로그로 출력
ESP_LOGI(TAG, "Read Data: %s (%d)", (char *)read_buf, strlen((char *)read_buf));
}
위의 소스에서 at24c04_read()에서 읽기를 바로하지 않고 write()를 통해 특정 위치를 지정하는 이유는, 메모리 어드레스를 읽고자 하는 위치옮기기 위함이다. 그러므로 위치를 옮긴후에 다시 재시작을 위해 i2c_master_start(cmd)를 호출하고 있다. 그리고 데이터를 읽으면서, NACK(Not Acknowledge)는 마지막 바이트를 읽어서 더이상 읽지 않음을 알리기 위한 신호를 보내게된다.
2. 결과 확인
결과 화면을 찍으려니.. 또 모듈이 안보인다. 찾는데로 업데이트 하겠다.
EEPROM은 전원이 차단되어도 데이터가 유지되는 특성을 가지고있는 메모리이다. 따라서, 전원이 꺼지더라도 유지하고 싶은데이터인 장치의이름이나 설정과 관련된 데이터를 넣게된다. 필자와 같이 ESP32,I2C,HelloWorld 이런식으로 컴마로 구분해놨다면, ESP32가 부팅되면서 EEPROM을 통해 데이터를 읽어 동작에 적용할 수 있다.
물론, 보안이 필요한 경우라면 데이터를 기록할 때 암호화를 해야되겠지만, 딱히... 그럴필요가 있을까? 가끔 있기도 하다. 어쨋든 이후에 WEB을 통해 사용자의 설정을 저장해야 한다면 EEPROM등을 이용해서 깔끔하게 처리가 가능하다.
최근댓글