티스토리 뷰
nRF 노르딕 칩에서 database로 활용하기 위해서는 flash를 활용해야하는데 별도의 다른 물리적 저장장치가 없기 때문입니다.. 아는 사람은 알겠지만 flash 공간은 wrtie, read 하는 속도도 느리고 제일 중요한 endurance라고 하는 최대 write 가능한 횟수가 그리 많지 않습니다. 우선 nrf52의 flash 영역의 상세 스펙을 확인해보도록 하겠습니다.
10000 이라는 숫자가 눈에 띕니다. 1000번정도 해당 영역이 write 될 수 있다는 뜻인데요, 따지고 보면 Write할때 Erase를 하기 때문에 이것 조차 부가적인 Wrtie Cycle로 인식되어 5000번정도 새로 쓸수 있는 구조라고 볼 수 있습니다. 5000번 자체가 적은 숫자는 아니지만 만약 Wrtie 하는 것이 크리티컬한 부분이 있다면 5000번이 하루에 10번 발생시 500 번 즉 1년 반 정도 지나면 문제가 생길 수 있는 구조라고 말 할 수 있습니다. 또한 속도를 보면 Wrtie시 41 마이크로 세컨드정도 걸리고 전체를 지우는 것은 165ms 정도 걸리는 것으로 나와있습니다. 사실 단순한 테스트로는 체감하기 어려우나, 지속적인 Throughput 등의 테스트를 진행하면 속도차이를 체감할 수 있습니다.
오늘은 이러한 flash의 한계적인 부분을 해결하는 방법을 다루지는 않고 쉽게 이용할 수 있는 방법에 대해서 설명하도록 하겠습니다. 이 후에 횟수 제한을 해결 할 수 있는 방안에 대해서 다뤄보는 글을 쓰도록 하겠습니다.
어떤 데이터를 DB 화 해야할까?
이전에 retained register에 대해서 내용을 작성 했었는데요, 못 보신 분은 아래 링크로 가보시기 바랍니다.
https://hero-space.tistory.com/86?category=1022576
retained register 자체는 특정 리셋의 경우에만 저장되는 단순한 레지스터인 반명 우리가 오늘 다룰 flash에 db화 하는 것은 non volatile 영역으로 파워가 나갔다 들어와도 유지되고 있는 영역입니다. 다만 이떤 데이터를 이곳에 저장해야 할까요? 상황에 따라 다르겠지만 껏다 키더라도 리셋되더라도 이어져 나가야 하는 값들일 수 있습니다. 이를 테면 사용자가 어떤 상태값으로 저장해서 사용하고 있는데 배터리가 나가서 전원이 종료되어 초기상태 값으로 세팅이 돌아간다면 사용성이 굉장히 떨어질 것이기 때문에 이럴때 db로 데이터가 날라가지 않게 보관하게 두는 것입니다. 또한 어느정도의 용량으로 db를 구성하느냐도 고민하는 포인트 인데요, 칩마다 flash 사이즈가 다르고, 필수적인 내용을 빼고 빈 곳의 사이즈를 보고 어느정도 미리 할당할지 결정이 필요합니다. 제가 이번에 사용할 nrf52810의 경우는 flash 영역이 192kb 이며 그중 사용 가능한 공간은 50K 정도인데 application 코드가 들어가는 걸 감안하면 여유공간이 많이 남지 않습니다. 그래서 가볍에 4K 정도만 할당하기로 결정하고 fstorage라는 인터페이스를 이용해서 미리 설정해보도록 하겠습니다.
NRF_FSTORAGE_DEF(nrf_fstorage_t fstorage) =
{
/* Set a handler for fstorage events. */
.evt_handler = _fstorage_evt_handler,
.start_addr = DB_START_ADDRESS,
.end_addr = DB_END_ADDRESS,
};
우선 전역으로 위와같이 정의합니다. fstorage는 구조체를 사용한 것으로 DB로 사용할 곳은 Start/End Address 를 미리 세팅할 수가 있습니다. 저는 여기서 DB_START_ADDRESS와 DB_END_ADDRESS를 아래와 같이 정했습니다. 1000헥사 값으로 환산하면 4096 바이트라 4Kbyte에 해당됩니다. 중간에 이야기 했던 4Kbyte 사이즈로 설정한 것이죠.
#define DB_START_ADDRESS 0x27000
#define DB_END_ADDRESS 0x27FFF
이후에 메인에서 fstorage를 위에 선한한 내용으로 init이 되도록 해줍니다,
void _flash_fstorage_init(void)
{
uint32_t err_code;
nrf_fstorage_api_t *p_fs_api;
p_fs_api = &nrf_fstorage_sd;
err_code = nrf_fstorage_init(&fstorage, p_fs_api, NULL);
APP_ERROR_CHECK(err_code);
}
메인에서 위와같은 init 을 호출하면 되는데 이때 보면 nrf_storage_init이라는 인터페이스를 콜해주고 있습니다. 아까 미리 선언한 구조체의 경우네는 콜백 함수가 달려있는데 콜백함수는 기본적으로 아래와 같이 정의해 둘 수 있습니다.
static void _fstorage_evt_handler(nrf_fstorage_evt_t * p_evt)
{
if(p_evt->result != NRF_SUCCESS)
{
DBG_LUPLE_PRINT("[%s] Error while executing an fstorage operation.\r\n", _FN_);
return;
}
switch(p_evt->id)
{
case NRF_FSTORAGE_EVT_READ_RESULT: // 0x00
DBG_LUPLE_PRINT(" READ DONE\r\n");
//printf("[%s] Read (0x%X, %d)\r\n", _FN_, p_evt->addr, p_evt->len);
break;
case NRF_FSTORAGE_EVT_WRITE_RESULT: // 0x01
DBG_LUPLE_PRINT(" WRITE DONE\r\n");
//printf("[%s] Write (0x%X, %d)\r\n", _FN_, p_evt->addr, p_evt->len);
break;
case NRF_FSTORAGE_EVT_ERASE_RESULT: // 0x02
DBG_LUPLE_PRINT(" ERASE DONE\r\n");
//printf("[%s] Erase (0x%X, %d)\r\n", _FN_, p_evt->addr, p_evt->len);
break;
default:
break;
}
}
이렇게 되면 선언한 영역을 요청한 행동에 따라 완료되면 콜백함수가 호출되니 로그를 넣어서 확인을 해보면 쉽습니다.
uint32_t _flash_fstorage_write(uint32_t addr, uint8_t *data, uint16_t size)
{
uint32_t err_code;
err_code = nrf_fstorage_write(&fstorage, addr, data, size, NULL);
APP_ERROR_CHECK(err_code);
return err_code;
}
uint32_t _flash_fstorage_read(uint32_t addr, uint8_t *data, uint16_t size)
{
uint32_t err_code;
err_code = nrf_fstorage_read(&fstorage, addr, data, size);
APP_ERROR_CHECK(err_code);
return err_code;
}
uint32_t _flash_fstorage_erase(uint32_t addr)
{
uint32_t err_code;
err_code = nrf_fstorage_erase(&fstorage, addr, 1, NULL);
APP_ERROR_CHECK(err_code);
return err_code;
}
Write, Read, Erase 모두 그렇게 어렵지 않고 nrf_fstorage 아래 있는 API를 사용하여 데이터를 저장하고 읽고 지우는 과정을 수행할 수 있습니다. Wrtie시에는, 저장할 곳의 주소값, 데이터의 포인터, 사이즈를 입력하게 됩니다. 물론 처음 지정한 사이즈가 있기 때문에 넘지 않도록 중의하고 ALIGN(4) 가 되도록 해주는 것이 좋습니다. 아무래도 low할 물리적 메모리이고, 저장하는 것고 최적으로 사이즈를 워드단위또 바이트 단위로 할 수 있지만 데이터 저장단위는 4바이트 단위로 하는 것을 권장합니다.
이제 결론으로 가서, System Off Sleep화 wakeup 하는 과정과 단순히 software reset 하는 던순한 과정은 메인 코드가 실행될때 Retained Register를 통해서 구분해서 이후 분기를 타면 되고, 만약 여러가지 데이터를 저장하고 읽어야하는 경우 flash 를 DB화 하여 사용할 수 잇는 internal flash library인 fstorage를 위와같이 영역을 미리 설정하고 저장하는 쓸수 있는 모듈을 만들어 내면 여러군데에서 다양하게 사용 할 수 있습니다. 다만 조금은 low한 사용 usage때문에 실수 할 수 있으므로 주의하고, db라는 모듈로 래핑해서 fstorage말고 fds나 다른 library르 사용 할 수 있도록 한번 감싸주는 것이 library의 dependency를 조금 더 줄여주는 프로그래밍이 될 수 있습니다. 혹시 더 궁금하신분은 댓글로 남겨주시면 추가 설명 드리도록 하겠습니다.
'Technology > Bluetooth' 카테고리의 다른 글
전력 소비를 줄이는 방법 1 - DCDC vs LDO (0) | 2022.05.28 |
---|---|
nrfconnect 사용해서 nrf 칩에 올리기 준비 (0) | 2022.05.22 |
NRF Sleep 후 Wakeup 과 Reset 구별하기 (0) | 2022.05.15 |
전력관리를 위한 System Off/On 슬립 기능 구현 (0) | 2022.05.14 |
이클립스로 노르딕 블루투스 예제 실행하기2 (0) | 2022.05.12 |