/****************************************************************************** * * Copyright(c) 2007 - 2015 Realtek Corporation. All rights reserved. * * ******************************************************************************/ #include <platform_opts.h> #if CONFIG_EXAMPLE_SPI_ATCMD #include "FreeRTOS.h" #include "task.h" #include <platform/platform_stdlib.h> #include "semphr.h" #include "device.h" #include "osdep_api.h" #include "osdep_service.h" #include "device_lock.h" #include "spi_atcmd/example_spi_atcmd.h" #include "at_cmd/log_service.h" #include "at_cmd/atcmd_wifi.h" #include "at_cmd/atcmd_lwip.h" #include "flash_api.h" #include "spi_api.h" #include "spi_ex_api.h" #include "gpio_irq_api.h" #include "gpio_irq_ex_api.h" /**** SPI FUNCTIONS ****/ spi_t spi_obj; gpio_t gpio_cs; #define SPI_RX_BUFFER_SIZE ATSTRING_LEN/2 #define SPI_TX_BUFFER_SIZE ATSTRING_LEN/2 uint16_t spi_chunk_buffer[ATSTRING_LEN/2]; _Sema master_rx_done_sema; _Sema master_tx_done_sema; #define SPI_USE_STREAM (0) #define SPI_USE_DMA (1) /**** SLAVE HARDWARE READY ****/ gpio_t gpio_hrdy; #define SPI_SLAVE_BUSY 0 #define SPI_SLAVE_READY 1 volatile int spi_slave_status = SPI_SLAVE_BUSY; _Sema spi_check_hrdy_sema; #define BLOCKING 1 #define NONBLOCKING 0 volatile int hrdy_pull_down_counter = 0; /**** SLAVE SYNC ****/ gpio_irq_t gpio_sync; #define SPI_STATE_MISO 0 #define SPI_STATE_MOSI 1 int spi_state = SPI_STATE_MISO; /**** TASK THREAD ****/ _Sema spi_check_trx_sema; /**** LOG SERVICE ****/ char at_string[ATSTRING_LEN]; extern char log_buf[LOG_SERVICE_BUFLEN]; extern xSemaphoreHandle log_rx_interrupt_sema; #define LOG_TX_BUFFER_SIZE 48*1024 char log_tx_buffer[LOG_TX_BUFFER_SIZE]; uint32_t log_tx_idx = 0; uint32_t log_rx_idx = 0; /**** DATA FORMAT ****/ #define PREAMBLE_COMMAND 0x6000 #define PREAMBLE_DATA_READ 0x1000 #define PREAMBLE_DATA_WRITE 0x0000 #define COMMAND_DUMMY 0x0000 #define COMMAND_BEGIN 0x0304 #define COMMAND_END 0x0305 #define COMMAND_READ_BEGIN 0x0012 #define COMMAND_READ_RAW 0x0013 #define COMMAND_WRITE_BEGIN 0x0014 #define COMMAND_READ_WRITE_END 0x0015 #define REGISTER_ADDR 0x2000 void atcmd_update_partition_info(AT_PARTITION id, AT_PARTITION_OP ops, u8 *data, u16 len) { flash_t flash; int size, offset, i; u32 read_data; switch(id){ case AT_PARTITION_SPI: size = SPI_CONF_DATA_SIZE; offset = SPI_CONF_DATA_OFFSET; break; case AT_PARTITION_WIFI: size = WIFI_CONF_DATA_SIZE; offset = WIFI_CONF_DATA_OFFSET; break; case AT_PARTITION_LWIP: size = LWIP_CONF_DATA_SIZE; offset = LWIP_CONF_DATA_OFFSET; break; case AT_PARTITION_ALL: size = 0x1000; offset = 0; break; default: printf("partition id is invalid!\r\n"); return; } device_mutex_lock(RT_DEV_LOCK_FLASH); if(id == AT_PARTITION_ALL && ops == AT_PARTITION_ERASE){ flash_erase_sector(&flash, SPI_SETTING_SECTOR); goto exit; } if(ops == AT_PARTITION_READ){ flash_stream_read(&flash, SPI_SETTING_SECTOR+offset, len, data); goto exit; } //erase BACKUP_SECTOR flash_erase_sector(&flash, SPI_SETTING_BACKUP_SECTOR); if(ops == AT_PARTITION_WRITE){ // backup new data flash_stream_write(&flash, SPI_SETTING_BACKUP_SECTOR+offset, len, data); } //backup front data to backup sector for(i = 0; i < offset; i += sizeof(read_data)){ flash_read_word(&flash, SPI_SETTING_SECTOR + i, &read_data); flash_write_word(&flash, SPI_SETTING_BACKUP_SECTOR + i,read_data); } //backup rear data for(i = (offset + size); i < 0x1000; i += sizeof(read_data)){ flash_read_word(&flash, SPI_SETTING_SECTOR + i, &read_data); flash_write_word(&flash, SPI_SETTING_BACKUP_SECTOR + i,read_data); } //erase UART_SETTING_SECTOR flash_erase_sector(&flash, SPI_SETTING_SECTOR); //retore data to UART_SETTING_SECTOR from UART_SETTING_BACKUP_SECTOR for(i = 0; i < 0x1000; i+= sizeof(read_data)){ flash_read_word(&flash, SPI_SETTING_BACKUP_SECTOR + i, &read_data); flash_write_word(&flash, SPI_SETTING_SECTOR + i,read_data); } //erase BACKUP_SECTOR flash_erase_sector(&flash, SPI_SETTING_BACKUP_SECTOR); exit: device_mutex_unlock(RT_DEV_LOCK_FLASH); return; } /* AT cmd V2 API */ void spi_at_send_string(char *str) { spi_at_send_buf(str, strlen(str)); } /* AT cmd V2 API */ void spi_at_send_buf(u8 *buf, u32 len) { int i; int spi_tx_tail_next; if( !len || (!buf) ){ return; } if (buf == log_tx_buffer) { log_tx_idx = len; } else { for (i=0; i<len; i++) { if (log_tx_idx == LOG_TX_BUFFER_SIZE) { // overflow! break; } log_tx_buffer[log_tx_idx++] = buf[i]; } } if (__get_IPSR() != 0) { RtlUpSema(&spi_check_trx_sema); } else { RtlUpSemaFromISR(&spi_check_trx_sema); } } /* IRQ handler called when SPI TX/RX finish */ void master_trx_done_callback(void *pdata, SpiIrq event) { switch(event){ case SpiRxIrq: RtlUpSemaFromISR(&master_rx_done_sema); //DBG_8195A("Master RX done!\n"); break; case SpiTxIrq: //DBG_8195A("Master TX done!\n"); break; default: DBG_8195A("unknown interrput evnent!\n"); } } /* IRQ handler called when SPI TX finish */ static void master_tx_done_callback(uint32_t id) { RtlUpSemaFromISR(&master_tx_done_sema); } /* IRQ handler as gpio hrdy hit rising edge */ void slave_hrdy_change_callback(uint32_t id) { gpio_irq_disable(&gpio_hrdy); if (spi_slave_status == SPI_SLAVE_BUSY) { // Transition from LOW to HIGH. Change to listen IRQ_LOW spi_slave_status = SPI_SLAVE_READY; hrdy_pull_down_counter++; gpio_irq_set(&gpio_hrdy, IRQ_LOW, 1); gpio_irq_enable(&gpio_hrdy); } else { // Transition from HIGH to LOW. Change to listen IRQ_HIGH spi_slave_status = SPI_SLAVE_BUSY; gpio_irq_set(&gpio_hrdy, IRQ_HIGH, 1); gpio_irq_enable(&gpio_hrdy); } RtlUpSemaFromISR(&spi_check_hrdy_sema); } /* IRQ Handler as gpio sync state change */ void slave_sync_chagne_callback(uint32_t id) { gpio_irq_disable(&gpio_sync); if (spi_state == SPI_STATE_MISO) { // Transition from LOW to HIGH. Change to listen IRQ_LOW spi_state = SPI_STATE_MOSI; gpio_irq_set(&gpio_sync, IRQ_LOW, 1); gpio_irq_enable(&gpio_sync); } else { // Transition from HIGH to LOW. Change to listen IRQ_HIGH spi_state = SPI_STATE_MISO; gpio_irq_set(&gpio_sync, IRQ_HIGH, 1); gpio_irq_enable(&gpio_sync); } RtlUpSemaFromISR(&spi_check_trx_sema); } void spi_atcmd_main(void) { wifi_disable_powersave(); // read settings SPI_LOG_CONF spiconf; spiconf.bits = 16; spiconf.frequency = 20000000; spiconf.mode = (SPI_SCLK_IDLE_LOW|SPI_SCLK_TOGGLE_MIDDLE); // init spi spi_init(&spi_obj, SPI0_MOSI, SPI0_MISO, SPI0_SCLK, SPI0_CS); spi_frequency(&spi_obj, spiconf.frequency); spi_format(&spi_obj, spiconf.bits, spiconf.mode, 0); spi_bus_tx_done_irq_hook(&spi_obj, master_tx_done_callback, (uint32_t)&spi_obj); spi_irq_hook(&spi_obj, master_trx_done_callback, (uint32_t)&spi_obj); // init simulated spi cs gpio_init(&gpio_cs, GPIO_CS); gpio_dir(&gpio_cs, PIN_OUTPUT); gpio_mode(&gpio_cs, PullNone); gpio_write(&gpio_cs, 1); // init gpio for check if spi slave hw ready gpio_init(&gpio_hrdy, GPIO_HRDY); gpio_dir(&gpio_hrdy, PIN_INPUT); gpio_mode(&gpio_hrdy, PullDown); gpio_irq_init(&gpio_hrdy, GPIO_HRDY, slave_hrdy_change_callback, (uint32_t)&gpio_hrdy); gpio_irq_set(&gpio_hrdy, IRQ_HIGH, 1); gpio_irq_enable(&gpio_hrdy); // init gpio for check if spi slave want to send data gpio_irq_init(&gpio_sync, GPIO_SYNC, slave_sync_chagne_callback,(uint32_t)&gpio_sync); gpio_irq_set(&gpio_sync, IRQ_HIGH, 1); gpio_irq_enable(&gpio_sync); // init semaphore for check hardware ready RtlInitSema(&spi_check_hrdy_sema, 1); RtlDownSema(&spi_check_hrdy_sema); // init semaphore that makes spi tx/rx thread to check something RtlInitSema(&spi_check_trx_sema, 1); RtlDownSema(&spi_check_trx_sema); // init semaphore for master tx RtlInitSema(&master_tx_done_sema, 1); RtlDownSema(&master_tx_done_sema); // init semaphore for master rx RtlInitSema(&master_rx_done_sema, 1); RtlDownSema(&master_rx_done_sema); } int32_t spi_master_send(spi_t *obj, char *tx_buffer, uint32_t length) { hrdy_pull_down_counter = 0; spi_master_write_stream_dma(obj, tx_buffer, length); RtlDownSema(&master_tx_done_sema); if (spi_slave_status == SPI_SLAVE_BUSY) { while (hrdy_pull_down_counter == 0); hrdy_pull_down_counter = 0; } } int32_t spi_master_recv(spi_t *obj, char *rx_buffer, uint32_t length) { hrdy_pull_down_counter = 0; spi_flush_rx_fifo(obj); spi_master_read_stream_dma(obj, rx_buffer, length); RtlDownSema(&master_rx_done_sema); RtlDownSema(&master_tx_done_sema); if (spi_slave_status == SPI_SLAVE_BUSY) { while (hrdy_pull_down_counter == 0); hrdy_pull_down_counter = 0; } } void atcmd_check_special_case(char *buf) { int i; if (strlen(buf) > 4) { if (strncmp(buf, "ATPT", 4) == 0) { for (i=0; i<strlen(buf); i++) { if (buf[i] == ':') { buf[i] = '\0'; break; } } } } } static void spi_trx_thread(void *param) { uint32_t i; uint32_t rxlen, txlen; uint32_t recv_len, recv_remain, send_len; uint16_t dummy, L_address, H_address, L_size, H_size; int slave_ready = 0; do { slave_ready = gpio_read(&gpio_hrdy); rtw_msleep_os(1000); } while(slave_ready == 0); while(1) { RtlDownSema(&spi_check_trx_sema); if (spi_state == SPI_STATE_MOSI) { if (log_tx_idx > 0) { /* Slave hw is ready, and Master has something to send. */ // stage A, read target address txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_COMMAND; spi_chunk_buffer[txlen++] = COMMAND_BEGIN; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); gpio_write(&gpio_cs, 0); spi_chunk_buffer[0] = PREAMBLE_DATA_READ; spi_master_send(&spi_obj, spi_chunk_buffer, 1 * 2); spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); dummy = spi_chunk_buffer[0]; spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); L_address = spi_chunk_buffer[0]; spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); H_address = spi_chunk_buffer[0]; spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); L_size = spi_chunk_buffer[0]; spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); H_size = spi_chunk_buffer[0]; gpio_write(&gpio_cs, 1); // stage B, write data txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_COMMAND; spi_chunk_buffer[txlen++] = COMMAND_WRITE_BEGIN; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); if (log_tx_idx % 2 != 0) { log_tx_buffer[log_tx_idx++] = 0; } send_len = log_tx_idx / 2; L_size = send_len & 0x0000FFFF; H_size = (send_len & 0xFFFF0000) >> 16; gpio_write(&gpio_cs, 0); txlen = 1; spi_chunk_buffer[0] = PREAMBLE_DATA_WRITE; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_chunk_buffer[0] = L_address; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_chunk_buffer[0] = H_address; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_chunk_buffer[0] = L_size; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_chunk_buffer[0] = H_size; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); gpio_write(&gpio_cs, 0); txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_DATA_WRITE; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); txlen = log_tx_idx/2; spi_master_send(&spi_obj, log_tx_buffer, txlen * 2); // sending raw data gpio_write(&gpio_cs, 1); // stage C, write data end txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_COMMAND; spi_chunk_buffer[txlen++] = COMMAND_READ_WRITE_END; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); // stage final txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_COMMAND; spi_chunk_buffer[txlen++] = COMMAND_END; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); L_size = log_tx_idx & 0x0000FFFF; H_size = (log_tx_idx & 0xFFFF0000) >> 16; txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_DATA_WRITE; spi_chunk_buffer[txlen++] = L_size; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_DATA_WRITE; spi_chunk_buffer[txlen++] = H_size; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); // finalize log_tx_idx = 0; } } else if (spi_state == SPI_STATE_MISO) { /* Slave hw is ready, and Slave want to send something. */ do { // stage A, read target address txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_COMMAND; spi_chunk_buffer[txlen++] = COMMAND_BEGIN; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_DATA_READ; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); dummy = spi_chunk_buffer[0]; spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); L_address = spi_chunk_buffer[0]; spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); H_address = spi_chunk_buffer[0]; spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); L_size = spi_chunk_buffer[0]; spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); H_size = spi_chunk_buffer[0]; gpio_write(&gpio_cs, 1); recv_len = ((H_size << 16) | L_size); if (recv_len == 0) { break; } // Stage B, confirm addr & len txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_COMMAND; spi_chunk_buffer[txlen++] = COMMAND_READ_BEGIN; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); gpio_write(&gpio_cs, 0); txlen = 1; spi_chunk_buffer[0] = PREAMBLE_DATA_WRITE; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_chunk_buffer[0] = L_address; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_chunk_buffer[0] = H_address; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_chunk_buffer[0] = L_size; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_chunk_buffer[0] = H_size; spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); // Stage C, begin to read txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_COMMAND; spi_chunk_buffer[txlen++] = COMMAND_READ_RAW; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_DATA_READ; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); spi_master_recv(&spi_obj, spi_chunk_buffer, 1 * 2); // recv dummy rxlen = recv_len; spi_master_recv(&spi_obj, log_buf, rxlen * 2); log_buf[rxlen*2]= '\0'; gpio_write(&gpio_cs, 1); // Stage D, read end txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_COMMAND; spi_chunk_buffer[txlen++] = COMMAND_READ_WRITE_END; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); // stage final txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_COMMAND; spi_chunk_buffer[txlen++] = COMMAND_END; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); L_size = (recv_len) & 0x0000FFFF; H_size = ((recv_len) & 0xFFFF0000) >> 16; txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_DATA_WRITE; spi_chunk_buffer[txlen++] = L_size; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); txlen = 0; spi_chunk_buffer[txlen++] = PREAMBLE_DATA_WRITE; spi_chunk_buffer[txlen++] = H_size; gpio_write(&gpio_cs, 0); spi_master_send(&spi_obj, spi_chunk_buffer, txlen * 2); gpio_write(&gpio_cs, 1); // finalize //printf("%s", log_buf); atcmd_check_special_case(log_buf); RtlUpSema(&log_rx_interrupt_sema); taskYIELD(); } while (0); } } vTaskDelete(NULL); } static void spi_atcmd_thread(void *param) { p_wlan_init_done_callback = NULL; atcmd_wifi_restore_from_flash(); atcmd_lwip_restore_from_flash(); rtw_msleep_os(20); spi_atcmd_main(); // the rx_buffer of atcmd is to receive and sending out to log_tx atcmd_lwip_set_rx_buffer(log_tx_buffer, sizeof(log_tx_buffer)); at_set_debug_mask(0x0); if(xTaskCreate(spi_trx_thread, ((const char*)"spi_trx_thread"), 4096, NULL, tskIDLE_PRIORITY+1 , NULL) != pdPASS) printf("\n\r%s xTaskCreate(spi_trx_thread) failed", __FUNCTION__); vTaskDelete(NULL); } int spi_atcmd_module_init(void){ if(xTaskCreate(spi_atcmd_thread, ((const char*)"spi_atcmd_thread"), 1024, NULL, tskIDLE_PRIORITY+1 , NULL) != pdPASS) printf("\n\r%s xTaskCreate(spi_atcmd_thread) failed", __FUNCTION__); return 0; } void example_spi_atcmd(void) { p_wlan_init_done_callback = spi_atcmd_module_init; return; } #endif // #if CONFIG_EXAMPLE_SPI_ATCMD