1. Unboxing Contents:

Issue: Internal packaging is very detailed, but the outer carton is too soft, risking hardware damage.

2. Environment Setup

Using VScode for compilation. Refer to: Beginner Guide: Setting up AiPi-R2 Windows Environment” on Ai-Thinker Forum.

Encountered issue during compilation:

Fix: Open aithinker_Ai-M6X_SDK\project.build, comment out lines 75 and 76 by adding # at the beginning and save.

#        cp $(BL_SDK_BASE)/bsp/board/bl616dk/config/edata.bin build/build_out 

#        cp $(BL_SDK_BASE)/bsp/board/bl616dk/config/Rx_boot2_m61.bin build/build_out 


3. Example Testing

No onboard LED (recommended to add one), making it hard to verify board operation without a display.

LVGL Demo: Reflashed official LVGL example using make for compilation and make flash COMX=COM3 for flashing (COM3 is USB-TTL port).

Re-burn the LVGL official routines. Use make to compile the code and make flash COMX=COM3 to burn it. COM3 is the port currently connected to the development board. Use USB to TTL. The connection diagram is as follows:

4. LVGL Integration & Testing

LVGL was customized based on the AiPi-Eyes-Rx example. After GUI design using GuiGuider, generate and replace the generated folder under demos\ai_lvgl\src\generated.

5. SPI TF Card Reading & FATFS Filesystem

Wired the onboard SPI to a TF card module (or solder a TF slot). Adjust pin definitions accordingly.

Key SPI code: Refer to

aithinker_Ai-M6X_SDK\examples\peripherals\spi\spi_poll,SPI configuration and TF card initialization code included in the document.

void spi_isr(int irq, void *arg)

{

    uint32_t intstatus = bflb_spi_get_intstatus(spi0);

    if (intstatus & SPI_INTSTS_TC) {

        bflb_spi_int_clear(spi0, SPI_INTCLR_TC);

        //printf("tc done\r\n");

        spi_tc_done_count++;

    }

    if (intstatus & SPI_INTSTS_TX_FIFO) {

        //printf("tx fifo\r\n");

    }

    if (intstatus & SPI_INTSTS_RX_FIFO) {

        //printf("rx fifo\r\n");

    }

}


void SPI_init(u32 freqm)

{

    struct bflb_spi_config_s spi_cfg =

    {

#if (SPI_CASE_SELECT == SPI_MASTER_CASE)

        .freq = freqm,

        .role = SPI_ROLE_MASTER,

#else

        .freq = 32 * 1000 * 1000,

        .role = SPI_ROLE_SLAVE,

#endif

        .mode = SPI_MODE3,

        .data_width = SPI_DATA_WIDTH_8BIT,

        .bit_order = SPI_BIT_MSB,

        .byte_order = SPI_BYTE_LSB,

        .tx_fifo_threshold = 0,

        .rx_fifo_threshold = 0,

    };

    spi0 = bflb_device_get_by_name("spi0");

    bflb_spi_init(spi0, &spi_cfg);

    bflb_spi_tcint_mask(spi0, false);

    bflb_irq_attach(spi0->irq_num, spi_isr, NULL);

    bflb_irq_enable(spi0->irq_num);

    bflb_spi_feature_control(spi0, SPI_CMD_SET_CS_INTERVAL, 0);


}

// Modify board.h and add the following:

void board_spi0_gpio_init()

{

    struct bflb_device_s* gpio;

 

    gpio = bflb_device_get_by_name("gpio");

    // spi cs

    bflb_gpio_init(gpio, GPIO_PIN_20, GPIO_FUNC_SPI0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);

    // spi clk

    bflb_gpio_init(gpio, GPIO_PIN_17, GPIO_FUNC_SPI0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);

    // spi miso

    bflb_gpio_init(gpio, GPIO_PIN_22, GPIO_FUNC_SPI0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);

    // spi mosi

    bflb_gpio_init(gpio, GPIO_PIN_15, GPIO_FUNC_SPI0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);

}

// Below is the SPI initialization and SD card read code:

u8 SD_Initialize(void)

{

    u8 r1; // Response from SD card

    u16 retry; // Retry counter

    u8 buf[4];

    u16 i;

    SPI_init(281000); // Set to low speed

 

    SD_DisSelect();

    for(i=0;i<10;i++)

    {

        SD_SPI_ReadWriteByte(0XFF); // Send at least 74 clock cycles

    }

    retry=20;

    do

    {

        r1=SD_SendCmd(CMD0,0,0x95); // Enter IDLE state

    } while((r1!=0X01) && retry--);

    SD_DisSelect();

    SD_Type=0; // Default: no card detected

 

    if(r1==0X01)

    {

        r1=SD_SendCmd(CMD8,0x1AA,0x87);

        if(r1==1)

        {

            for(i=0;i<4;i++)

            {

                buf[i]=SD_SPI_ReadWriteByte(0xFF);

            }

            SD_DisSelect();

            if(buf[2]==0X01&&buf[3]==0XAA)

            {

                retry=0XFFFE;

                do

                {

                    r1=SD_SendCmd(CMD55,0,0x01); // Send CMD55

                    r1=SD_SendCmd(0x69,0x40000000,0x01); // Send CMD41

                    SD_DisSelect();

                } while(r1&&retry--);

 

                if(retry && SD_SendCmd(CMD58,0,0X01)==0)

                {

                    for(i=0;i<4;i++)

                    {

                        buf[i]=SD_SPI_ReadWriteByte(0xFF);

                    }

                    SD_DisSelect();

                    if(buf[0]&0x40)

                        SD_Type=SD_TYPE_V2HC;

                    else

                        SD_Type=SD_TYPE_V2;

                }

            }

        }

        else // SD V1.x / MMC V3

        {

            SD_SendCmd(CMD55,0,0X01);

            r1=SD_SendCmd(CMD41,0,0X01);

            SD_DisSelect();

            if(r1<=1)

            {

                SD_Type=SD_TYPE_V1;

                retry=0XFFFE;

                do

                {

                    SD_SendCmd(CMD55,0,0X01);

                    r1=SD_SendCmd(CMD41,0,0X01);

                    SD_DisSelect();

                } while(r1&&retry--);

            }

            else // MMC card does not support CMD55+CMD41

            {

                SD_Type=SD_TYPE_MMC;

                retry=0XFFFE;

                do

                {

                    r1=SD_SendCmd(CMD1,0,0X01);

                    SD_DisSelect();

                } while(r1&&retry--);

            }

            r1=SD_SendCmd(CMD16,512,0X01);

            SD_DisSelect();

            if(retry==0 || r1!=0)

            {

                SD_Type=SD_TYPE_ERR;

            }

        }

    }

    SD_DisSelect();

    SPI_init(16000000); // Switch to high speed

    if(SD_Type)

        return 0;

    else if(r1)

        return r1;

    return 0xaa; // Other error

}

 

u8 SD_SPI_ReadWriteByte(u8 data)

{

    u8 retnum;

    Spi_Send_Read_Data_Arr(&data, &retnum, 1);

    return retnum;

}

 

void SD_DisSelect(void)

{

    SD_SPI_ReadWriteByte(0XFF); // Send 8 extra clock cycles

}

 

u8 SD_WaitReady(void)

{

    u32 t = 0;

    u8 rStr = 0;

    do

    {

        rStr = SD_SPI_ReadWriteByte(0xFF);

        if(rStr == 0XFF)

            return 0; // Ready

        t++;

    } while(t < 0XFFFFFF);

    return 1; // Timeout

}

 

u8 SD_Select(void)

{

    if(SD_WaitReady() == 0)

        return 0; // Ready

    SD_DisSelect();

    return 1; // Failed

}

 

u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)

{

    u8 r1;

    u8 Retry = 0;

    SD_DisSelect();

    if(SD_Select()) return 0XFF; // Select failed

 

    SD_SPI_ReadWriteByte(cmd | 0x40);

    SD_SPI_ReadWriteByte((u8)(arg >> 24));

    SD_SPI_ReadWriteByte((u8)(arg >> 16));

    SD_SPI_ReadWriteByte((u8)(arg >> 8));

    SD_SPI_ReadWriteByte((u8)(arg));

    SD_SPI_ReadWriteByte(crc);

 

    if(cmd == 12)

        SD_SPI_ReadWriteByte(0xff); // Dummy byte for CMD12

 

    Retry = 0X1F;

    do

    {

        r1 = SD_SPI_ReadWriteByte(0xFF);

    } while((r1 & 0X80) && Retry--);

    return r1;

}

If you have the system time, you can enter it. If not, just use a random one.

Other files in the file system can be copied directly to the folder and used. The specific interface is as follows:

FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);                                /* Open or create a file */

FRESULT f_close (FIL* fp);                                                                                        /* Close an open file object */

FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br);                        /* Read data from the file */

FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw);        /* Write data to the file */

FRESULT f_lseek (FIL* fp, FSIZE_t ofs);                                                                /* Move file pointer of the file object */

FRESULT f_truncate (FIL* fp);                                                                                /* Truncate the file */

FRESULT f_sync (FIL* fp);                                                                                        /* Flush cached data of the writing file */

FRESULT f_opendir (DIR* dp, const TCHAR* path);                                                /* Open a directory */

FRESULT f_closedir (DIR* dp);                                                                                /* Close an open directory */

FRESULT f_readdir (DIR* dp, FILINFO* fno);                                                        /* Read a directory item */

FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern);        /* Find first file */

FRESULT f_findnext (DIR* dp, FILINFO* fno);                                                        /* Find next file */

FRESULT f_mkdir (const TCHAR* path);                                                                /* Create a sub directory */

FRESULT f_unlink (const TCHAR* path);                                                                /* Delete an existing file or directory */

FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new);        /* Rename/Move a file or directory */

FRESULT f_stat (const TCHAR* path, FILINFO* fno);                                        /* Get file status */

FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask);                        /* Change attribute of a file/dir */

FRESULT f_utime (const TCHAR* path, const FILINFO* fno);                        /* Change timestamp of a file/dir */

FRESULT f_chdir (const TCHAR* path);                                                                /* Change current directory */

FRESULT f_chdrive (const TCHAR* path);                                                                /* Change current drive */

FRESULT f_getcwd (TCHAR* buff, UINT len);                                                        /* Get current directory */

FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs);        /* Get number of free clusters on the drive */

FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn);        /* Get volume label */

FRESULT f_setlabel (const TCHAR* label);                                                        /* Set volume label */

FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf);        /* Forward data to the stream */

FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt);                                        /* Allocate a contiguous block to the file */

FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);                        /* Mount/Unmount a logical drive */

FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len);        /* Create a FAT volume */

FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work);                /* Divide a physical drive into some partitions */

FRESULT f_setcp (WORD cp);                                                                                        /* Set current code page */

int f_putc (TCHAR c, FIL* fp);                                                                                /* Put a character to the file */

int f_puts (const TCHAR* str, FIL* cp);                                                                /* Put a string to the file */

int f_printf (FIL* fp, const TCHAR* str, ...);                                                /* Put a formatted string to the file */

TCHAR* f_gets (TCHAR* buff, int len, FIL* fp);                                                /* Get a string from the file */

6. Wi-Fi Connection and TCP/IP Communication

For Wi-Fi connection initialization and connection code, refer to: aithinker_Ai-M6X_SDK\examples\wifi\sta\wifi_tcp. Key code to note:

uint8_t wifi_connect(char* ssid, char* passwd)

{

    int ret = 255;

    // struct fhost_vif_ip_addr_cfg ip_cfg = { 0 };

    uint32_t ipv4_addr = 0;

    scan_item_cb_t resd;

    char we1;

    char we2;

    if (NULL==ssid || 0==strlen(ssid)) {

        return 1;

    }

    if (wifi_mgmr_sta_state_get() == 1) {

        wifi_sta_disconnect();

    }

    printf("wait000");

    //wifi_mgmr_scan_ap_all(&we1,&we2,resd);

    if (wifi_sta_connect(ssid, passwd, NULL, NULL, 0, 0, 0, 1))

    //if (wifi_mgmr_sta_quickconnect(ssid, passwd,  0, 0))

    {

        return 4;

    }

    LOG_I("Wating wifi connet");

    //Wait for the connection to succeed

    sta_ConnectStatus = 0;

    for (int i = 0;i<10*30;i++) {

        vTaskDelay(100/portTICK_PERIOD_MS);

        switch (sta_ConnectStatus) {

            case CODE_WIFI_ON_MGMR_DONE:

                return 3;

            case CODE_WIFI_ON_SCAN_DONE:

                return 2;

            case CODE_WIFI_ON_DISCONNECT:        //Connection failed (the number of retries has exceeded but the connection is not successful)

                return 4;

            case CODE_WIFI_ON_CONNECTED:        //)Connection successful (when the Wi-Fi status is STA, it means that the IP (DHCP) is successfully obtained, or a static IP is used)

                LOG_I("Wating wifi connet OK");

                break;

            case CODE_WIFI_ON_GOT_IP:

                return 0;

            default:

                //Wait for the connection to succeed

                break;

        }

    }

    return 14; //Connection timeout

}


int wifi_start_firmware_task(void)
{
    LOG_I("Starting wifi ...");
    /* enable wifi clock */
    GLB_PER_Clock_UnGate(GLB_AHB_CLOCK_IP_WIFI_PHY | GLB_AHB_CLOCK_IP_WIFI_MAC_PHY | GLB_AHB_CLOCK_IP_WIFI_PLATFORM);
    GLB_AHB_MCU_Software_Reset(GLB_AHB_MCU_SW_WIFI);
    /* set ble controller EM Size */
    GLB_Set_EM_Sel(GLB_WRAM160KB_EM0KB);
    if (0 != rfparam_init(0, NULL, 0)) {
        LOG_I("PHY RF init failed!");
        return 0;
    }
    LOG_I("PHY RF init success!");
    /* Enable wifi irq */
    extern void interrupt0_handler(void);
    bflb_irq_attach(WIFI_IRQn, (irq_callback)interrupt0_handler, NULL);
    bflb_irq_enable(WIFI_IRQn);
    xTaskCreate(wifi_main, (char*)"fw", WIFI_STACK_SIZE, NULL, TASK_PRIORITY_FW, &wifi_fw_task);
    return 0;
}
/**
* @brief wifi event handler
*      WiFi Event callback
*
* @param code
*/
void wifi_event_handler(uint32_t code)
{
    sta_ConnectStatus = code;
    BaseType_t xHigherPriorityTaskWoken;
    switch (code) {
        case CODE_WIFI_ON_INIT_DONE:
        {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE", __func__);
            wifi_mgmr_init(&conf);
        }
        break;
        case CODE_WIFI_ON_MGMR_DONE:
        {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE", __func__);
        }
        break;
        case CODE_WIFI_ON_SCAN_DONE:
        {
            wifi_mgmr_sta_scanlist();
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE SSID numbles:%d", __func__, wifi_mgmr_sta_scanlist_nums_get());
        }
        break;
        case CODE_WIFI_ON_CONNECTED:
        {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED", __func__);
            void mm_sec_keydump();
            mm_sec_keydump();
        }
        break;
        case CODE_WIFI_ON_GOT_IP:
        {
            ui_load_scr_animation(&guider_ui, &guider_ui.screen_1, guider_ui.screen_1_del, &guider_ui.screen_del, setup_scr_screen_1, LV_SCR_LOAD_ANIM_OVER_LEFT, 0, 0, true, true);
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP", __func__);
        }
        break;
        case CODE_WIFI_ON_DISCONNECT:
        {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT", __func__);
        }
        break;
        case CODE_WIFI_ON_AP_STARTED:
        {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED", __func__);
        }
        break;
        case CODE_WIFI_ON_AP_STOPPED:
        {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED", __func__);
        }
        break;
        case CODE_WIFI_ON_AP_STA_ADD:
        {
            LOG_I("[APP] [EVT] [AP] [ADD] %lld", xTaskGetTickCount());
        }
        break;
        case CODE_WIFI_ON_AP_STA_DEL:
        {
            LOG_I("[APP] [EVT] [AP] [DEL] %lld", xTaskGetTickCount());
        }
        break;
        default:
        {
            LOG_I("[APP] [EVT] Unknown code %u ", code);
        }
    }
}

 

The calling method is as follows:

Start the thread in the main function

tcpip_init(NULL, NULL); wifi_start_firmware_task();

char * wifi_code;
                    char * wifi_name;
                    wifi_name = lv_textarea_get_text(guider_ui.screen_ta_1);
                    wifi_code = lv_textarea_get_text(guider_ui.screen_ta_2);
                    wifi_connect(wifi_name,wifi_code);
Here, we retrieve the Wi-Fi name and password from the text boxes on the LVGL screen.

The TCP/IP reference code is located in the same location as the Wi-Fi code. The key code is as follows:

void wifi_test_tcp_client_init(int argc, char **argv)
{
    abort_exec = shell_signal(SHELL_SIGINT, test_close_client);
    printf("tcp client task start ...\r\n");
    char *addr;
    char *port;
    struct sockaddr_in remote_addr;
    if (argc < 3) {
        printf("%s", PING_USAGE);
        return;
    }
printf("111...\r\n");
    /* get address (argv[1] if present) */
    addr = "192.168.31.236";//argv[1];
    /* get port number (argv[2] if present) */
    port = "8888";//argv[2];
    while (1) {
        printf("222...\r\n");
        if ((sock_client = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            printf("TCP Client create socket error\r\n");
            return;
        }
        printf("333...\r\n");
        remote_addr.sin_family = AF_INET;
        remote_addr.sin_port = htons(atoi(port));
        remote_addr.sin_addr.s_addr = inet_addr(addr);
        memset(&(remote_addr.sin_zero), 0, sizeof(remote_addr.sin_zero));
printf("444...\r\n");
        printf("Server ip Address : %s:%s\r\n", addr, port);
        if (connect(sock_client, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) != 0) {
            printf("TCP client connect server falied!\r\n");
            closesocket(sock_client);
            return;
        }
        printf("TCP client connect server success!\r\n");
        printf("Press CTRL-C to exit.\r\n");
        total_cnt = 0;
        while (1) {
            if (write(sock_client, send_buf, sizeof(send_buf)) < 0) {
                printf("write falied!\r\n");
                break;
            }
            total_cnt += sizeof(send_buf);
            vTaskDelay(500);
        }
        closesocket(sock_client);
        return;
    }
}

The calling method is as follows:

Invocation:

 

7. Summary & Development Ideas

Current test results:

Future plans: