페이지

글목록

2016년 2월 12일 금요일

[dialog] DA14580 외부 FLASH 메모리에 쓰기

안녕하세요,

DA-14580 BLE CPU는 OTP 메모리와 RAM으로 구성되어 있어서,
1번 잘못 쓰면 프로그램을 수정할 수가 없습니다.

그래서 Keil debug를 통해서 RAM에 프로그램을 올려서 테스트용으로 프로그램을 실행하던가,
아니면 외부에 SPI/I2C Flash Memory를 붙여서 JTAG를 통해서 Dialog S/W tool인 
SmartSnippets을 사용해서 프로그램을 Flash Memory에 쓸 수 있습니다.

동작 방식은 전원을 켜면 SPI/I2C Flash 메모리에서 프로그램을 읽어와서 
SRAM에 copy 해서 SRAM에서 프로그램이 돌아가는 형태입니다.

저는 SPI FLASH를 사용했는데,
DA14580에서 약간의 I/O를 사용하는데, 가뜩이나 작은 칩을 사용해서 I/O가 충분히 많지 않기 때문에
SPI를 사용할 경우 특정 핀(SPI FLASH 연결 핀들)을 피해서 사용해야 합니다.

프로그램은 Debug에서 돌리던 프로그램을 그대로 올리면 되고,
대신에 전원 리셋시, SPI FLSH에서 S-RAM으로 프로그램을 Copy하는 시간 때문인지 약간의 딜레이가 생깁니다.


위의 표를 보면 secondary bootloader 를 사용해 보진 않아서 모르겠지만,
OTP에 프로그램을 굽는 것 보다 외부 플래쉬에 프로그램을 쓰는 것이 시간과 에너지 소비가 훨 씬 많네요.

[dialog] DA14580 개발용 보드 소개

다이얼 로그사의 개발용 키트의 종류는 다음과 같습니다.
제가 사용하는 개발용 키트는 Dialog 사의 Expert 키트 입니다.
Expert kit 는 내부에 SEGGER 칩이 내장되어 있지 않아서 디버거를 따로 제공합니다.
다른 두개의 보드(BASIC,PRO)는 내부에 디버거가 내장되어 있으니 좀 더 편리해진 것 같습니다.

다음은 EXPERT 보드의 총 구성품목 입니다.


2016년 2월 11일 목요일

[dialog] SUOTA Update at DSPS Profile

^^ ㅎㅎ.

약간의 꼼수를 이용하여 DSPS 프로그램을 Update 하는 방법을 알아냈습니다.

간략하게 말씀드리면 secondary bootloader 와 proximity 프로그램 2개를 멀티로 초기에 SPI FLASH에 올리고
2번 BANK에 DSPS 프로그램을 SUOTA로 Update 하면 DSPS가 동작합니다.

그럼 1번 뱅크에는 처음에 이미 저장된 proximity 프로그램이 있고, 현재 동작은 DSPS 프로그램이 동작하고 있겠죠?

만약 다음번에 DSPS 프로그램을 업데이트 하고 싶으면 어떻게 해야 할지 생각해 봤습니다.
DSPS 프로그램 내에서 DSPS 프로그램이 위치하고 있는 영역의 SPI 영역을 지워버렸더니, 
리셋 후, secondary bootloader가 자동으로 BANK1의 proximity 프로그램을 생행을 하는군요.

그렇다고 함은, 다시 SUOTA 앱이 동작하는 것을 의미하죠.

이 때, 다시 BANK2에 DSPS 프로그램을 업데이트 하니까 되더군요.

이렇게 하면 ,  어떤 프로그램이라도 특정 영역(BANK2)을 지우는 기능만 있으면 모두 SUOTA를 사용해서 무선 업데이트가 가능하리라 생각이 됩니다.

이상입니다. 조금 더 자세한 설명은 오늘 저녁쯤에 작업해서 업데이트하겠습니다.

[dialog] DA14580 보드 Address 에 관하여.

현재 DA-14580 으로 BLE Beacon 을 개발 중입니다.

BLE (Bluetooth Low Energy) : 에너지 아주 적게 쓰는 Bluetooth 를 말하는 거겠죠?
BEACON : 비콘은 블루투스 통신 프로토콜의 일종인데, 매우 단순하고 데이터 전송량도 많지 않습니다.
     단순히 몇가지 정보를 주기적으로 전달하고 끝. 자세한 정보는 구글링에서 더 찾아보시고요. ^^

DA-14580 으로 각 IC 마다, Uniq 한 ID값이 있으면 좋은데
그런게 없네요. 

IC를 구울 때 마다 
(1) 소스코드에서 프로그램 메모리에 수정해서 넣던가 
(2) JTAG로 NVDS 값을 넣는 OTP 메모리 영역에 Board Address 값을 넣어 줄 수가 있습니다.

(1)번 방법은 절차와 시간이 많이 걸리고,
(2)번 방법은 (1)에 비해 좀 수월하지만, 역시 별로네요. ㅜㅜ

어쩔 수 없이, (2)번 방법을 사용해야 할 것 같습니다. 으아.. 번호 넣다 깜박 졸면 OTP라 다시 쓸 수가 없으니
칩을 버려야 하는 단점이 있네요.


다른 방법으로는 SPI 또는 I2C 방식의 Flash 메모리를 외부에 사용해서 (1)의 방법을 사용할 수도 있습니다. 

내부에 Flash 메모리가 있는 DA-14583 부터는 잘 못 써도 다시 쓸 수 있겠네요.



하지만 아쉬운 점이, 보드 어드레스는 제품 출하때부터 써서 나왔으면 하네요.

[dialog] DA14580 다이얼로그사 BLE용 Cortex-M0

안녕하세요,

이번엔 다이얼로그사의 BLE 용 Cotex M0 를 소개합니다.

개발 툴은 좀 비싼것을 회사에서 샀죠. Expert 였나?
찾아 보니 Expert 맞네요.

BLE용 IC는 DA-14580 입니다. 현재 DA-14581,14582,14583,14680 까지 총 5개의 제품이 나와 있습니다.

개발자용 웹사이트 주소는 다음과  같습니다.

위 사이트 가서 회원가입하고 승인이 떨어져야, 여러가지 문서와 압축 화일을 받을 수 있기 때문에
자료를 함부로 올릴 수는 없겠습니다.

좀 폐쇄적인데,왜 이렇게 운영을 하는지 모르겠는데요.. 흠.. 

앞으로 개발자 사이트 외에 구글링해서 공개된 자료나 텍스트로와 링크로만 글을 작성해야 할 것 같습니다. 

아, 일반에게 공개된 사이트가 또 있는데..
자료가 좀 제한적이긴 합니다. 



각각의 칩의 Feature 만 두고 차이를 알아볼까요?

[ DA-14580 ]
  • Complete Bluetooth® Smart SoC (4.1 specification)
  • 32-bit ARM Cortex M0 microcontroller
  • Radio current consumption: 4.9 mA RX / TX at 3 V at 0 dBm output power
  • Under 600 nA sleep mode
  • -93 dBm Rx sensitivity
  • 0.9 – 3.45 V operating range
  • Single-pin antenna interface
  • Up to 32 GPIOs
  • Supporting Software Upgrade Over The Air (SUOTA)

[ DA-14581 ]
  • Bluetooth® Smart (4.1 specification)
  • Cortex M0 application processor
  • Power supply 0.9 – 3.6 V
  • Single pin RF I/O
  • Rich set of analog and digital peripherals
  • 8 connections
  • Optimized boot time
  • Supporting Software Upgrade Over The Air (SUOTA)
da-14580 과 다른 점은 
- Power supply 0.9 – 3.6 V
- Rich set of analog and digital peripherals
- 8 connections
- Optimized boot time

[ DA-14582 ]
  • Bluetooth 4.1
  • Cortex M0 application processor
  • Power supply 2.35 -3.3 V
  • Single pin RF I/O
  • Rich set of analog and digital peripherals
  • Wideband analog codec input and output
  • Compatible with FR1-type PCB
  • Supporting software upgrade Over The Air (OTA)
da-14581 과 다른 점은 
- Power supply 2.35 – 3.3 V
- Wideband analog codec input and output
- Compatible with FR1-type PCB
- Supporting software upgrade Over The Air (OTA)

[ DA-14583 ]
  • Bluetooth 4.1
  • Cortex M0 application processor
  • 1 Mb Flash
  • Power supply 2.35 -3.3 V
  • Single pin RF I/O
  • Rich set of analog and digital peripherals
  • Supporting software upgrade Over The Air (OTA)
da-14582 과 다른 점은 
1 Mb Flash


[ DA-14680 ]
  • Bluetooth 4.2
  • Cortex M0 application processor
  • Dedicated HW crypto engine
  • Integrated battery and system PMU
  • Power supply 1.7 -5 V
  • Single pin RF I/O
  • Rich set of analog and digital peripherals
  • 8 Mb executable Flash
da-14583 과 다른 점은 
- Bluetooth 4.2
- Dedicated HW crypto engine
- Integrated battery and system PMU
- Power supply 1.7 -5 V
- 8 Mb executable Flash



전체적으로 봤을 때,
1. DA14580 에서 P1.2,P1.3 을 빠르게 토글하면 통신상 이상이 있던 점이 이후 버전부터 수정되었고,
2. power supply 가 0.9V~3.6V 에서 중간에 2.35V~3.3V 로 갔다가 최종 1.7V~5V 로 바뀌었네요.
3. 내부 프로그램 메모리가 OTP에서 플레쉬로 DA-14583 부터 바뀌었고요,
4. DA-14680에서 bluetooth 4.2로 바뀌네요.


2016년 2월 10일 수요일

[dialog] IBEACON 프로토콜로 전환 방법

일반적으로 BEACON 이란,
보통 “신호를 발신하는 장치”라는 의미로 쓰이는 단어입니다. 비행기의 아래쪽에 있는 깜빡이는 등과 고층빌딩이나 피뢰침 같은 높은 구조물에 설치되는 깜빡이는 등, 바다에 부표에 역시 깜빡이는 등을 본 적이 있을 것입니다. 이것들을 Beacon(비콘)이라고 부릅니다.
그런데 사실 이 등들은 빛만 나오는 것이 아니라 전파 신호를 함께 보내고 있습니다. 영화 ‘오블리비언’에서 톰 크루즈가 안테나가 있는 건물에서 무엇을 작동시켰더니 갑자기 스페이스 셔틀이 내려오면서 본격적으로 영화가 시작되는데 이때 작동시킨 것이 비콘인 것입니다. 우주에 있던 셔틀이 비콘 신호를 수신하고 그 신호를 따라 지구에 착륙한 것이죠. 비콘은 이처럼 자신이 있는 곳의 위치를 깜빡이는 빛과 전파로 알려 충돌을 막고 유도하는 용도로 사용됩니다.



[비콘 프로토콜의 형식]
IBEACON이란,
ISO 를 사용하는 기기에서 사용하는 BEACON 프로토콜을 말합니다.

IOS는 APPLE의 운영체제이기 때문에 일반 Android 용 BEACON 프로토콜에서 
Company ID 만 APLLE로 바꿔주면 IBEACON으로 인식합니다.

다른 용도로 만들어 놓은 이미지인데, 아래쪽 비콘 내용 중 Company ID를 APPLE사를 뜻하는 0x4C00 으로 하면
IBEACON(This is IBEACON!)으로 인식했습니다.

[dialog] JTAG 디버깅 모드에서 동작 안함.??

이번에는 Smart TAG 용 제품을 개발하기 시작했습니다.

Dialog사의 DA14580 을 사용해서 BLE 페어링,데이터 송/수신, SW Read, GPIO out 등을 테스트 할 예정입니다.

예전에 다루지 않고 넘어갔던 부분인데, DA14580 의 예제 코드는 그냥 디버깅 실행하면 동작하지 않습니다.

원인은 저도 초보인지라, 자세히는 모르지만
Sleep mode 로 들어가면 Jtag가 동작을 안하는것 같습니다.

나름대로 해결한 내용은 다음과 같습니다.
방법1 : DA14580 개발 보드에서 JTAG를 연결하고  DEBUG 버튼을 눌러서 실행을 하면, 어딘가에 막혀 동작하지 않는다.
arch_system.c 파일의 uint8_t check_sys_startup_period(void) 에서
while ((GetWord16(SYS_STAT_REG) & DBG_IS_UP) == DBG_IS_UP) {}; 에서 걸려 있었는데 주석처리 하면 디버그 모드에서도 계속 동작.

방법2 : Dialog 사의 지원팀으로부터 알게 된 사항입니다.
da14580_config.h 의 
/*Sleep modes*/
//#define CFG_EXT_SLEEP  
#undef CFG_EXT_SLEEP  
#undef CFG_DEEP_SLEEP  

#define CFG_EXT_SLEEP   를 #undef CFG_EXT_SLEEP  로 수정하면 간단히 해결된다.

디버그 모드에서 정상 동작 테스트를 하기 위해서는 주의할 내용이다.

[dialog] DSPS Profile 에서 Connect 신호 추가하기

보통 BLE to UART 모듈들이 BLE가 연결 됐는지를 알려주는 출력 신호를 제공하지요? (다른 모듈을 안써봐서 모르겠네요. ^^)

그런데 저는 이런 기능이 1개 있으면 좋겠다는 생각에 BLE가 연결되고 끊어지는 부분을 여기저기 찾아봐서 알아 냈습니다.

동작은 잘 되더군요.

먼저 연결되는 부분은 다음과 같습니다.

app_sps_device_project.c 의 
void app_connection_func(struct gapc_connection_req_ind const *param)
{
    /*--------------------------------------------------------------
    * ENABLE REQUIRED PROFILES
    *-------------------------------------------------------------*/
    //enable SPS device role or SPS host and set the proper values
    app_sps_enable();
    
    gattc_exc_mtu_cmd();
  GPIO_SetActive(BLE_CON_PORT,BLE_CON_PIN);     // LED ON
  GPIO_SetInactive(BLE_CON1_PORT,BLE_CON1_PIN);  // CON PORT LOW
   
}

그 담으로 끊어지는 부분은 다음과 같습니다.

app_sps_device_project.c 의 
void app_disconnect_func(ke_task_id_t task_id, struct gapc_disconnect_ind const *param)
{
    uint8_t state = ke_state_get(task_id);
  GPIO_SetInactive(BLE_CON_PORT,BLE_CON_PIN);   // LED OFF
  GPIO_SetActive(BLE_CON1_PORT,BLE_CON1_PIN);  // CON PORT HIGH
   if ((state == APP_SECURITY) || (state == APP_CONNECTED)  || (state == APP_PARAM_UPD))
    {
        // Restart Advertising
        app_adv_start();
    }
    else
    {
        // We are not in a Connected State
        ASSERT_ERR(0);
    }
}


이렇게 하면 연결되면 CON Port가 High가 되고, 끊어지면 Con Port가 LOW 가 되서 외부 MCU에서 확인을 하고 BLE로 송수신을 할 수 있습니다.

[dialog] SUOTA 테스트

SUOTA 란 Software Update Over The Air 의 약자입니다.
이것은 The secondary bootloader 의 일종인데, 부트로더가 OTP ROM에 있고 이미지가 FLASH Memory에 있는 Scheme 1 과
SPI FLASH 에 부트로더와 이미지가 있는 Scheme 2 가 있습니다.

이 SUOTA를 지원하는 Profile은 proximity,Smart Tag,Beacon 등이 있습니다.
DSPS 도 지원을 해 줬으면 좋겠는데, 아직까지 방법을 못 찾았습니다. 하지만 1번은 SUOTA로 DSPS 이미지를 업데이트 됩니다만, 1번 하면 SUOTA 동작이 다시는 안됩니다. (나중에 방법을 찾아볼 예정입니다.)

http://support.dialog-semiconductor.com/ 사이트에 들어가서 AN-B-010 DA14580 using SUOTA.pdf 를 그대로 따라서 하면 됩니다.

예제가 3.08로 되어 있는데, 저는 SDK 5.03으로 성공했습니다.


H/W 점퍼를 SPI로 설정하고, 버전 3.08에서는 nvds.c 에서 NVDS_TAG_DEVICE_NAME = "SUOTA-1“ 로 고치라고 되어 있는데,
SDK 5.03에서는 user_config.h 의    #define USER_DEVICE_NAME    "SUOTA-1" 을 고치면 됩니다.

이것만 빼고는 다른 것이 없습니다.

그리고 visual studio 를 PC에 깔아야합니다. 실행화일로 프로그램을 주지 않고 소스 코드와 프로젝트 파일로 utillity를 제공하니까 컴파일해서 실행화일을 만들게 되어 있으니까요.

프로그램을 다 만들어서 multi_part.bin 을 smartsnipets 툴로 SPI Flash Memory에 Write 하고 (이 때, 팝업창이 뜨는데, No를 눌러줘야 동작합니다) SUOTA AP을 받아서 설치하시고, 메뉴얼 대로 따로 만들어 놓은 이미지를 스마트폰에 copy하여 BANK 1이나 2에 Update 하면 동작합니다.

테스트 겸, DSPS도 이미지로 만들어서 Upadate 해 봤는데 1번 실행되면 이후로 SUOTA 앱에서 장치이름이 안보여서 업데이트를 할 수가 없더군요. (제 생각에는 스위치나 Erase 명령으로 DSPS 이미지를 SPI Flash에서 지워 버리면 또 1개의 SUOTA 이미지가 다시 동작하지 않을까 합니다)

나중에, 다른 프로파일에서도 되도록 고쳐 볼 예정입니다. ^^

메뉴얼에 따라서 command mode에서 파일을 만들어 본 내용입니다.


2016년 2월 7일 일요일

[dialog] SPI memory 를 data memory로 이용하기

지난번 DSPS 프로그램에, Data memory 용 spi 읽기/쓰기를 적용해 봤습니다.

저는 SPI Flash 메모리를 주로 쓰는데, 메모리 용량이 2Mb(256KByte) 나 있으므로 남는 공간이 많습니다. 이 메모리는 처음 부팅시에만 쓰므로 어떻게 하면 사용할 방법이 없을까 하고 고민해 보니 EEPROM 처럼 필요한 데이터를 저장하는 용도로 쓸 수 있겠다 싶어서 테스트 해 봤는데, 잘 되는군요.

안그래도 da14580 은 EEPROM이 없어서 필요한 정보를 전원이 꺼지면 유지할 수가 없어서, EEPROM을 새로 달아야하나 했는데, 이걸로 해결이 되는 군요.

w25x20cl_a02 메모리(2Mbit) 의 제일 마지막 sector(0x3f000)를 사용했고,
Flash memory라 데이터를 쓰기 위해서는 Erase를 먼저 하고 써야 써집니다.
최소 Erase동작은 sector 단위(4KByte)로 할 수 있습니다.
그래서 마지막 섹터의 메모리 어드레스(0x3f000~0x3ffff) 를 지운다음 Write 합니다.

동작만 되는지 확인을 하다 보니 코드가 많이 지저분해 졌습니다.

테스트하기 위해 추가한 함수는 다음과 같습니다.
void app_spi_write_test(void)
{
    uint16_t man_dev_id;
    int16_t i;

wr_data[0] = '0';
for(i = 1 ; i < 512 ; i++)
{
wr_data[i] = wr_data[i-1] + 1;
}
    // Enable FLASH and SPI
    spi_flash_peripheral_init();
    // spi_flash_chip_erase();
    // Read SPI Flash Manufacturer/Device ID
    man_dev_id = spi_read_flash_memory_man_and_dev_id();
    
    spi_cs_low();
    
    spi_cs_high();
    
    spi_flash_write_data(wr_data, 0x3f000, 512);
}

void app_spi_erase_test(void)
{
    // Enable FLASH and SPI
    spi_flash_peripheral_init();
spi_flash_block_erase(0x3f000,SECTOR_ERASE);
}

void app_spi_read_test(void)
{
    int16_t btrd;
    int16_t i;
    uint16_t read_size = 256;  // set this variable to the desired read size
    // Enable FLASH and SPI
    spi_flash_peripheral_init();
    btrd = spi_flash_read_data(rd_data, 0x3f000, read_size);
}

static void spi_flash_peripheral_init()
{
    spi_FLASH_CS_Pad.pin = SPI_CS_PIN;
    spi_FLASH_CS_Pad.port = SPI_GPIO_PORT;
    // Enable SPI & SPI FLASH

    spi_init(&spi_FLASH_CS_Pad, SPI_MODE_8BIT, SPI_ROLE_MASTER, SPI_CLK_IDLE_POL_LOW, SPI_PHA_MODE_0, SPI_MINT_DISABLE, SPI_XTAL_DIV_8);

    detected_spi_flash_device_index = spi_flash_auto_detect();

    if(detected_spi_flash_device_index == SPI_FLASH_AUTO_DETECT_NOT_DETECTED)
    {
        // The device was not identified.
        // The default parameters are used (SPI_FLASH_SIZE, SPI_FLASH_PAGE)
        // Alternatively, an error can be asserted here.
        spi_flash_init(SPI_FLASH_SIZE, SPI_FLASH_PAGE);
    }
}

이전 프로토콜 테스트 하던 코드에 다음을 추가하여,
스마트폰으로 erase,write,read 테스트를 해 봤습니다.
void app_user_callback(void)
{
        //get data and pointer
        //uint8_t size = app_uart_pull(user_tx_write_pointer, TX_CALLBACK_SIZE, &tx_state_ptr);
int size;
char try_test[100];
while(1)
{
size = app_item_count(&user_buffer);
if (size >= RX_USER_CALLBACK_SIZE)
{
app_user_pull(rx_user_read_pointer, 1);
if (rx_user_read_pointer[0] == '[')
{
app_user_pull(rx_user_read_pointer, RX_USER_CALLBACK_SIZE-1);
if (strncmp(rx_user_read_pointer,"cmd",3) == 0)
{
sprintf(try_test,"cmd recieved OK\n\r");
app_ble_push(try_test,17); // UART Tx out
user_BLE_txbuffer(try_test, 17); // BLE Tx out
}
else if (strncmp(rx_user_read_pointer,"rqm",3) == 0)
{
sprintf(try_test,"rqm recieved OK\n\r");
user_BLE_txbuffer(try_test, 17); // BLE Tx out
app_spi_read_test();
user_BLE_txbuffer(rd_data, 20); // BLE Tx out
}
else if (strncmp(rx_user_read_pointer,"wrm",3) == 0)
{
sprintf(try_test,"wrm recieved OK\n\r");
user_BLE_txbuffer(try_test, 17); // BLE Tx out
app_spi_write_test();
}
else if (strncmp(rx_user_read_pointer,"era",3) == 0)
{
sprintf(try_test,"era recieved OK\n\r");
user_BLE_txbuffer(try_test, 17); // BLE Tx out
app_spi_erase_test();
}
else
{
sprintf(try_test,"cmd recieved FAIL\n\r");
app_ble_push(try_test,19);         // UART Tx out
user_BLE_txbuffer(try_test, 19); // BLE Tx out
}
break;
}
}
else
{
break;
}
}
}

테스트 결과,
Write를 하니 메모리에 0123456.... 을 쓰도록 했는데, 쓰고 나서 읽었더니 제대로 써 진것을 확인하였습니다.


프로그램 소스는 다음 링크에 있습니다.


2016년 2월 6일 토요일

[dialog] BLE protocol Test with DSPS profile (3/3)

이번에는 스마트폰에 DSPS 앱을 깔아서 DA14580 과 서로 연결한 후, 특정 프로토콜에 응답하는 프로그램을 만들어 보겠습니다.

물론 기본 DSPS는 동작하고, 추가로 기능을 넣었습니다.

이전에는 BLE에서 데이터가 들어온 것처럼 속여서 UART로 내보냈는데, 이번에는 UART에서 데이터가 들어온 것으로 속이면 BLE로 데이터가 전송이 되겠죠?

그런데 이번에는 조금 더 신경 쓸 내용이 있습니다. BLE에서 받은 데이터가 그대로 UART로 나가 버리므로 어떻게 BLE에서 수신된 데이터를 가로채야 할지 막막했습니다.

여러가지 생각을 해 보다가, 그냥 버퍼를 하나 더 만들었습니다. 그리고 나서 , BLE 수신 링버퍼에 데이터를 집어 넣을 때 만든 버퍼에도 넣었(push)습니다.
이렇게 하니 BLE에서 받은 데이터가 새로 만든 링버퍼에 들어가서 자유롭게 BLE로부터 수신된 데이터를 분석할 수 있었습니다.

난 나쁜놈인가? da14580을 속이고 가로채고 등쳐먹는 느낌이라, 왠지 기분이 좋지 않네요. ^^. 

1. BLE로 부터 데이터를 가로채기 위해서 먼저, 링버퍼를 하나 만듭니다. DSPS에서 링버퍼 struct 와 함수를 지원하니 같은 타입으로 1개 만들었습니다.
app_sps_scheduler.c 의 윗 부분으로 가서 다음과 같이 user_buffer 라는 이름으로 1개 만들었습니다.
/// SPS application buffer pointers
RingBuffer bletouart_buffer __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
RingBuffer uarttoble_buffer __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
RingBuffer user_buffer __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY

2. 또, 이 링버퍼로부터 데이터를 몇개 가져와서 처리하는데 사용할 byte 타입 버퍼도 한개 추가합니다.
/// UART callback pointers
uint8_t tx_write_pointer[TX_CALLBACK_SIZE+1] __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTIONMEMORY
uint8_t rx_read_pointer[RX_CALLBACK_SIZE] __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
uint8_t rx_user_read_pointer[RX_USER_CALLBACK_SIZE+1] __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
uint8_t tx_state_ptr __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
uint8_t rx_state_ptr __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY 

3. 아직은 버퍼가 만들어진 것은 아니고, app_sps_scheduler.c 의 다음과 같이 app_buffer_init 함수를 통해 초기화와 링버퍼를 만듭니다.
void app_buffer_init(void)
{
    if(!app_buffer_initialized(&bletouart_buffer) && !app_buffer_initialized(&uarttoble_buffer) &&!app_buffer_initialized(&user_buffer) )
    {
        //initialize buffers
        app_buffer_create(&bletouart_buffer, TX_BUFFER_ITEM_COUNT, TX_BUFFER_ITEM_SIZE, TX_BUFFER_LWM, TX_BUFFER_HWM);
        app_buffer_create(&uarttoble_buffer, RX_BUFFER_ITEM_COUNT, RX_BUFFER_ITEM_SIZE, RX_BUFFER_LWM, RX_BUFFER_HWM);
        app_buffer_create(&user_buffer, RX_BUFFER_ITEM_COUNT, RX_BUFFER_ITEM_SIZE, RX_BUFFER_LWM, RX_BUFFER_HWM);
    }
}

4. app_sps_scheduler.c 의 함수들이 static type들이라서 다른곳에서 함수를 사용하려면 h 파일에 프로토타입을 선언해야하고, 어쩌다 보면 중복 선언되서 에러도 나서 app_sps_scheduler.c 에 함수를 만들었습니다.

5. 새로 만든 링버퍼에 데이터를 집어(push)넣는 app_user_push()함수
void app_user_push(uint8_t* wrdata, uint8_t writeAmount)
{
    //write items to buffer;
    app_write_items(&user_buffer, wrdata, writeAmount);
}

6. 링버퍼에서 데이터를 끄집어(pull) 내는 app_user_pull 함수 
static uint8_t app_user_pull(uint8_t* rddata, uint8_t readamount)
{
    uint8_t readcount = 0;
        //pull data
        readcount = app_read_items(&user_buffer, rddata, readamount);
    return readcount;
}

7. da14580 을 속여서 uart 버퍼에 데이터를 집어넣어서 BLE에 출력하게 하는 user_BLE_txbuffer 함수
void user_BLE_txbuffer(uint8_t* wrdata, uint8_t writeAmount)
{
app_write_items(&uarttoble_buffer,wrdata,writeAmount);
}

8. 다음은 타이머 인터럽트 callback 함수에 넣고, 일정 시간마다 BLE로부터 수신된 사용자 링버퍼 데이터를 분석해서 처리하는 app_user_callback 함수도 만듭니다.
이 함수는 제가 단순하게 테스트를 위해 만든 함수로, 아주 어설프게 만들었으므로... 쓰시고자 하는 분들이 적절하게 좀 더 고쳐서 잘 만들면 되겠습니다. ^^
일단은, 전체 프로토콜 사이즈를 검사해서 스트링 사이즈가 RX_USER_CALLBACK_SIZE 보다 크면 
첫번째 캐릭터를 1개 링버퍼에서 꺼내서 '[' 캐릭터가 있다면 나머지 스트링도 다 끄집어 내어 2,3,4 번째 캐릭터가 "cmd"이면 잘 수신되었다고 BLE로 송신하는 내용입니다.

 void app_user_callback(void)
{
        //get data and pointer
        //uint8_t size = app_uart_pull(user_tx_write_pointer, TX_CALLBACK_SIZE, &tx_state_ptr);
int size;
char try_test[100];
 while(1) { size = app_item_count(&user_buffer); if (size >= RX_USER_CALLBACK_SIZE) { app_user_pull(rx_user_read_pointer, 1); if (rx_user_read_pointer[0] == '[') { app_user_pull(rx_user_read_pointer, RX_USER_CALLBACK_SIZE-1); if (strncmp(rx_user_read_pointer,"cmd",3) == 0) { sprintf(try_test,"cmd recieved OK\n\r"); app_ble_push(try_test,17); // UART Tx out user_BLE_txbuffer(try_test, 17); // BLE Tx out } else { sprintf(try_test,"cmd recieved FAIL\n\r"); app_ble_push(try_test,19); // UART Tx out user_BLE_txbuffer(try_test, 19); // BLE Tx out } break; } } else { break; } } 
}


9. 제일 중요한 부분을 빼 먹었군요. 실제로 BLE로 받은 데이터를 사용자 버퍼로 가져오는 부분인데, ble로부터 받은 데이터를 ble 버퍼에 집어넣는 함수와 같은 위치에 넣으면 됩니다.
찾아보니 gattc_write_cmd_ind_handler 함수 안에 있어서 사용자 버퍼에 집어넣는 함수를 같이 위치해 놓았습니다.
이렇게 하면 BLE로 수신된 데이터가 ble 버퍼와 사용자 버퍼에 동시에 들어갑니다.
static int gattc_write_cmd_ind_handler(ke_msg_id_t const msgid,
                                      struct gattc_write_cmd_ind const *param,
                                      ke_task_id_t const dest_id,
                                      ke_task_id_t const src_id)
{    
    if (param->handle == sps_server_env.shdl + SPS_SERVER_TX_DATA_CFG) 
    {
        //send write response if required
        if (param->response==0x01)
        {
            atts_write_rsp_send(sps_server_env.con_info.conidx, param->handle, PRF_ERR_OK);
        }
    }
    else if (param->handle == (sps_server_env.shdl + SPS_SERVER_RX_DATA_VAL))
    {
        // send data to the application
        app_ble_push((uint8_t*)&(param->value[0]), param->length);
        app_user_push((uint8_t*)&(param->value[0]), param->length);
    }
    else if (param->handle == (sps_server_env.shdl + SPS_SERVER_RX_DATA_CFG))
    {
        //send write response if required
        if (param->response==0x01)
        {
            atts_write_rsp_send(sps_server_env.con_info.conidx, param->handle, PRF_ERR_OK);
        }
    }
    else if (param->handle == sps_server_env.shdl + SPS_FLOW_CTRL_VAL)
    {
        //update ble flow control flag
        app_update_transmit_allowed(&ble_flags, param->value[0]);
    }
    else if (param->handle == sps_server_env.shdl + SPS_FLOW_CTRL_CFG)
    {
        //provide ble flow control state when flow control att is configured to 0x0100
        if (param->value[0]==0x01 && param->value[1]==0x00)
        {
            struct sps_server_request_flow_control_ind * msg = KE_MSG_ALLOC(SPS_SERVER_REQ_FLOW_CONTROL_IND, sps_server_env.appid,
                                                    TASK_SPS_SERVER, sps_server_request_flow_control_ind);
    
            ke_msg_send(msg);
        }
        //send write response if required
        if (param->response==0x01)
        {
            atts_write_rsp_send(sps_server_env.con_info.conidx, param->handle, PRF_ERR_OK);
        }
    }
    return (KE_MSG_CONSUMED);
}


10. 자 마지막으로 periph_setup.c의  timer callback 함수에 프로토콜 처리함수인 app_user_callback 함수를 실행하도록 하면 됩니다.
void timer0_general_user_callback_function(void) 
{
    static uint8_t n = 0;
    static uint32_t cnt_100ms = 0;
uint8_t try_test[100];
uint16_t port_Input=0;

cnt_100ms++;
    // when pass  10 * 100ms
    if ( 10 == n ) 
    {
port_Input = GPIO_GetPinStatus(GPIO_PORT_0,GPIO_PIN_6) + 
                          ((GPIO_GetPinStatus(GPIO_PORT_1,GPIO_PIN_1))<<1 span="">

sprintf((char *)try_test,"time = %8d ms , Port state : %01X\n\r\0",(cnt_100ms*100),port_Input);
app_ble_push(try_test,size_user_str(try_test));
app_user_callback();
       n = 0;
        //timeout_expiration--;
        if (GPIO_GetPinStatus(LED_PORT, LED_PIN))
        {
            GPIO_SetInactive(LED_PORT, LED_PIN);
        }
        else
        {
            GPIO_SetActive(LED_PORT, LED_PIN);
        }
     }
     n++;
}

11. 테스트 결과

동작 잘 돼네요. ^^


파일은 다음 링크에 첨부되어 있습니다.

[dialog] I/O control, UART Tx Write Test with DSPS (DA14580) (2/3)

이번에는 지난 번에 만들어 놓은 Timer callbak 함수 내에 일정한 주기로 포트를 읽어서 입력된 포트값의 상태를 읽어서 UART에 Write 하는 동작을 테스트 해 보겠습니다.

먼저, 입력 포트를 추가합니다.

periph.c 의 GPIO_reservations 함수에 다음을 추가합니다. Expert Kit 의 스위치는 2개가 있는데, P0_6 과 P1_1 이어서 이 2개의 포트를 추가합니다.

void GPIO_reservations(void)
{
   RESERVE_GPIO(SW0, GPIO_PORT_0, GPIO_PIN_6, PID_GPIO);    // SW IN(K2)
   RESERVE_GPIO(SW1, GPIO_PORT_1, GPIO_PIN_1, PID_GPIO);    // SW IN(K1)
   
   RESERVE_GPIO(LED, LED_PORT, LED_PIN, PID_GPIO);    // LED OUT

   RESERVE_GPIO( UART1_TX, UART1_TX_PORT, UART1_TX_PIN, PID_UART1_TX);  
   RESERVE_GPIO( UART1_RX, UART1_RX_PORT, UART1_RX_PIN, PID_UART1_RX);
#if (UART_HW_FLOW_ENABLED) 
   RESERVE_GPIO( UART1_RTS, UART1_RTS_PORT, UART1_RTS_PIN, PID_UART1_RTSN);  
   RESERVE_GPIO( UART1_CTS, UART1_CTS_PORT, UART1_CTS_PIN, PID_UART1_CTSN);
#endif /*UART_HW_FLOW_ENABLED*/
}

set_pad_functions 함수에도 코드를 추가합니다.
void set_pad_functions(void)        // set gpio port function mode
{
   GPIO_ConfigurePin(GPIO_PORT_0, GPIO_PIN_6, INPUT_PULLUP, PID_GPIO, true);
   GPIO_ConfigurePin(GPIO_PORT_1, GPIO_PIN_1, INPUT_PULLUP, PID_GPIO, true);

   GPIO_ConfigurePin(LED_PORT, LED_PIN, OUTPUT, PID_GPIO, false);

    GPIO_ConfigurePin( UART1_TX_PORT, UART1_TX_PIN, OUTPUT, PID_UART1_TX, false );
    GPIO_ConfigurePin( UART1_RX_PORT, UART1_RX_PIN, INPUT_PULLUP, PID_UART1_RX, false );
#if (UART_HW_FLOW_ENABLED) 
    GPIO_ConfigurePin( UART1_RTS_PORT, UART1_RTS_PIN, OUTPUT, PID_UART1_RTSN, false );
    GPIO_ConfigurePin( UART1_CTS_PORT, UART1_CTS_PIN, INPUT_PULLUP, PID_UART1_CTSN, false );
#endif /*UART_HW_FLOW_ENABLED*/
}



다음으로, 지난번에 만들어 두었던 타이머 인터럽트에 의해 1초마다 콜백되는 timer0_general_user_callback_function 함수에 포트를 읽어서 UART로 송신하는 프로그램을 추가해 보겠습니다.

UART에 Write 하는 방법은 사실 좀 오랫동안 찾아 해메다가 알아냈습니다. 좀 편법적인듯도 하지만 동작은 잘 되네요. DSPS 프로그램은 버퍼가 2개 있는데 1)BLE 쪽 송/수신 버퍼 와 2)UART 송/수신 버퍼 입니다. DSPS 동작 특성은 BLE 로 받으면 무조건 UART로 보내게 되어 있으므로 BLE 수신 버퍼에 데이터를 Write 하면 UART에 출력됩니다. 이 버퍼는 링버퍼로 이루어져 있는데 아무 때나, 버퍼에 최대 용량 이내로 데이터를 넣어주면 UART로 출력되고, 다 나가면 버퍼는 비워집니다.

쉽게 말하면, da14580을 BLE에서 데이터가 들어온 것으로 속이는 것입니다.
BLE 수신시 링버퍼에 데이터를 넣는(push) 함수가 app_ble_push() 입니다.
이 함수에 전송할 데이터와 데이터 갯수를 전달해 주면 UART에 출력됩니다.

void timer0_general_user_callback_function(void) 
{
   static uint8_t n = 0;
   static uint32_t cnt_100ms = 0;
   uint8_t try_test[100];
   uint16_t port_Input=0;

   cnt_100ms++;
    // when pass  10 * 100ms
    if ( 10 == n ) 
    {
      port_Input = GPIO_GetPinStatus(GPIO_PORT_0,GPIO_PIN_6) + ((GPIO_GetPinStatus(GPIO_PORT_1,GPIO_PIN_1))<<1 span="">

      sprintf((char *)try_test,"time = %8d ms , Port state : %01X\n\r\0",(cnt_100ms*100),port_Input);

      app_ble_push(try_test,size_user_str(try_test));
       n = 0;
        if (GPIO_GetPinStatus(LED_PORT, LED_PIN))
        {
            GPIO_SetInactive(LED_PORT, LED_PIN);
        }
        else
        {
            GPIO_SetActive(LED_PORT, LED_PIN);
        }
     }
     n++;
}

자 그럼, 출력된 UART 를 터미날로 볼까요?


버튼 상태가 잘 읽히고, DA14580을 속여서 UART로 데이터가 잘 송신되고 있습니다. ^^

[dialog] IO control with da14580 DSPS profile (1/3)

안녕하세요, 이번에는 Dialog 의 DA14580 BLE chip으로 이미 만들어져 있는 DSPS 프로파일 프로젝트에 Timer,UART에 데이터 송신,BLE에 데이터 송신,Port Out/Input 테스트를 진행하겠습니다.

추가로 BLE로 DA14580이 직접 데이터를 수신하여 특정 명령어에 반응하도록 테스트 해 보겠습니다.

저는 DA14580 developement kit Expert 를 갖고 있습니다. (basic kit 가 더 좋은 것 같습니다. JTAG도 내장되어있고 SPI FLASH도 있고 싸고요. ^^. Expert 는 참고로 130만원. )

이 테스트는 JTAG로 그냥 Debug 모드로 SRAM에 올려서 테스트 했습니다.
UART에 점퍼 셋팅해 놓았고요. 이 외에 특이한 사항은 없습니다.

좀 프로그램이 다듬어지지 않았고, 걍 테테스트입니다. ^^

테스트 보드 사진입니다. (점퍼가 없어져서 선으로 이은 것 뿐, 특이 사항 없슴.)


먼저 GPIO 출력포트를 DSPS 기본 예제에 추가해 보겠습니다.

1. periph_setup.c 에 있는 void GPIO_reservations(void) 함수에 코드를 추가했습니다.

#if DEVELOPMENT_DEBUG

void GPIO_reservations(void)
{
RESERVE_GPIO(LED, LED_PORT, LED_PIN, PID_GPIO);    // LED OUT

RESERVE_GPIO( UART1_TX, UART1_TX_PORT, UART1_TX_PIN, PID_UART1_TX);
RESERVE_GPIO( UART1_RX, UART1_RX_PORT, UART1_RX_PIN, PID_UART1_RX);

#if (UART_HW_FLOW_ENABLED)
 RESERVE_GPIO( UART1_RTS, UART1_RTS_PORT, UART1_RTS_PIN, PID_UART1_RTSN);
RESERVE_GPIO( UART1_CTS, UART1_CTS_PORT, UART1_CTS_PIN, PID_UART1_CTSN);
#endif /*UART_HW_FLOW_ENABLED*/
}
#endif //DEVELOPMENT_DEBUG

(Dialog 기본프로그램은  GPIO 포트를 사용하려할때, GPIO_reservations에서 reserve 하더군요. )



2. set_pad_functions() 함수도 바꿔 줬습니다.

void set_pad_functions(void)        // set gpio port function mode
{
    GPIO_ConfigurePin(LED_PORT, LED_PIN, OUTPUT, PID_GPIO, false);

    GPIO_ConfigurePin( UART1_TX_PORT, UART1_TX_PIN, OUTPUT, PID_UART1_TX, false );
    GPIO_ConfigurePin( UART1_RX_PORT, UART1_RX_PIN, INPUT_PULLUP, PID_UART1_RX, false );
#if (UART_HW_FLOW_ENABLED)
    GPIO_ConfigurePin( UART1_RTS_PORT, UART1_RTS_PIN, OUTPUT, PID_UART1_RTSN, false );
    GPIO_ConfigurePin( UART1_CTS_PORT, UART1_CTS_PIN, INPUT_PULLUP, PID_UART1_CTSN, false );
#endif /*UART_HW_FLOW_ENABLED*/
}


자, 이렇게 설정하고 Timer Interrupt 동작을 한번 테스트 해 볼까요?

periph.c  의 periph_init 함수를 다음과 같이 수정합니다.
- 타이머 인터럽트 발생 주기를 100ms(RELOAD_100MS=20000)로 설정하고 타이머 인터럽트를 활성화합니다.

void periph_init(void)
{
// Power up peripherals' power domain
    SetBits16(PMU_CTRL_REG, PERIPH_SLEEP, 0);
    while (!(GetWord16(SYS_STAT_REG) & PER_IS_UP)) ;
//rom patch
patch_func();
//Init pads
set_pad_functions();
   
 SetBits16(CLK_PER_REG, UART1_ENABLE, 1);    // enable clock - always @16MHz
    // baudr=9-> 115k2
    // mode=3-> no parity, 1 stop bit 8 data length
 uart_sps_init(UART_SPS_BAUDRATE, 3); //exact baud rate defined in uart.h

   // Enable the pads
SetBits16(SYS_CTRL_REG, PAD_LATCH_EN, 1);

#if (EXT_SLEEP_ENABLED)
    app_scheduler_reinit();
#endif //EXT_SLEEP_ENABLED

    // Stop timer for enter settings
    timer0_stop();
    // register callback function for SWTIM_IRQn irq
    timer0_register_callback(timer0_general_user_callback_function);
    // Enable TIMER0 clock
    set_tmr_enable(CLK_PER_REG_TMR_ENABLED);
    // Sets TIMER0,TIMER2 clock division factor to 8, so TIM0 Fclk is F = 16MHz/8 = 2Mhz
    set_tmr_div(CLK_PER_REG_TMR_DIV_8);
    // clear PWM settings register to not generate PWM
    timer0_set_pwm_high_counter(NO_PWM);
    timer0_set_pwm_low_counter(NO_PWM);
    
    timer0_set_pwm_on_counter(RELOAD_100MS);
    timer0_enable_irq();
    timer0_start();


#if ((UART_HW_FLOW_ENABLED) && !defined(GPIO_DRV_IRQ_HANDLING_DISABLED))
    GPIO_RegisterCallback(GPIO0_IRQn, gpio0_callback);
    if(GPIO_GetPinStatus(UART1_CTS_PORT,UART1_CTS_PIN)==FALSE)
    {
        GPIO_EnableIRQ(UART1_CTS_PORT, UART1_CTS_PIN, GPIO0_IRQn, 0, 1, 0);
    }
    else if(GPIO_GetPinStatus(UART1_CTS_PORT,UART1_CTS_PIN)==TRUE)
    {
        GPIO_EnableIRQ(UART1_CTS_PORT, UART1_CTS_PIN, GPIO0_IRQn, 1, 0, 0);
    }
#endif //((UART_HW_FLOW_ENABLED) && !defined(GPIO_DRV_IRQ_HANDLING_DISABLED))
}

이렇게 하면  타이머 인터럽트가 걸리면 void timer0_general_user_callback_function(void) 함수를 호출하게 됩니다.


다음으로 timer0_general_user_callback_function 함수의 내용 입니다.
void timer0_general_user_callback_function(void)
{
    static uint8_t n = 0;
    static uint32_t cnt_100ms = 0;
uint8_t try_test[100];
uint16_t port_Input=0;

cnt_100ms++;
    // when pass  10 * 100ms
    if ( 10 == n )
    {
        n = 0;
        if (GPIO_GetPinStatus(LED_PORT, LED_PIN))
        {
            GPIO_SetInactive(LED_PORT, LED_PIN);
        }
        else
        {
            GPIO_SetActive(LED_PORT, LED_PIN);
        }
     }
     n++;
}


자, 이렇게 하면 어떻게 될까요?
LED_PORT 의 LED_PIN 이 토글하겠죠? 

정확히 말하면 타이머 인터럽트가 100ms 마다 발생하면, timer0_general_user_callback_function 함수가 호출되어 static 변수 n이 1씩 증가하다가 n이 10이되면(1초마다) 현재 LED에 연결되어 있는 GPIO 포트를 읽어서 1이면 0, 0이면 1로 토글하는 동작을 합니다.

이렇게 해서, DSPS에 Timer 와 GPIO 출력까지 추가해 봤습니다.

예제 소스 파일은 마지막(3/3)에 추가 하겠습니다.