在上一篇中,我們已教大家如何快速上手QCA4020 CDB 環境建置及SDK編碼與燒錄,接下來我們要介紹利用QCA4020 CDB開發版上的元件來做I/O應用。
1. 目的
瞭解GPIO的Input、Output、Interrupt與I2C的設定及應用,首先我們需要知道這次使用的元件在QCA4020 CDB開發版上的相對位置(圖1)。
- GPIO Input與Interrupt是使用Button(S6)。
- GPIO Output是使用GPIO 20,由於我們會需要使用LED(Green)所以依(圖2)所示,需將J16的pin 11與pin12短路。
- I2C應用是使用Humidity And Temperature(U73),QCA4020 CDB開發版上採用ST HTS221電容器數位感測器。
(圖1) |
(圖2) |
2. 實例功能說明
我們會利用QCA4020 CDB上的Button(S6)來當作觸發點,當按下Button(S6),會將LED(Green)點亮(圖3),同時會經由Humidity And Temperature(U73) 讀取濕溫度顯示在putty上(圖4)。
(圖3) |
(圖4) |
3. 程式說明
首先可先下載我為大家提供的範例AITg_IO_example.7z,接下來我為大家說明部分比較重要的程式代碼。
a ) AITg IO example Code 內容
AITg_IO_example |-- src | |-- export | |-- qcli | | |-- pal.c //主體函數 | |-- AITg_button.c //GPIO button與LED功能 | |-- AITg_humidity_temperature.c //I2C讀取溫濕度功能 | |-- AITg_humidity_temperature.h | |-- sbrk.c |-- build | |-- gcc | | |-- build.bat //編譯程式使用 |
b ) app_init(qbool_t ColdBoot)
大家一開始入手編寫程式時一定會先尋找main()主程式在哪邊,QCA4020並不是使用大家常見的main(),而是使用app_init(qbool_t ColdBoot),所以我將Initialize_Button_Demo()與Initialize_Humidity_Temperature_Demo()這兩個初始設定寫在app_init(qbool_t ColdBoot)裡面。
由於Initialize_Button_Demo()與Initialize_Humidity_Temperature_Demo()分別在不同檔案,所以需要使用”extern”將他們變成外部變數即可使用。
extern qapi_Status_t Initialize_Button_Demo(void); extern qapi_Status_t Initialize_Humidity_Temperature_Demo(void); void app_init(qbool_t ColdBoot) //初始化函數 { /* Initialize the platform. */ if(PAL_Initialize()) { /* Initiailze the CLI. */ if(QCLI_Initialize()) { /* Create a receive event. */ qurt_signal_init(&(PAL_Context.Event)); /* Initialize the samples. */ Initialize_Humidity_Temperature_Demo(); //溫溼度初始化 Initialize_Button_Demo(); //Button 與 Led 初始化 PAL_Context.Initialized = true; } else { PAL_CONSOLE_WRITE_STRING_LITERAL("QCLI initialization failed"); PAL_CONSOLE_WRITE_STRING_LITERAL(PAL_OUTPUT_END_OF_LINE_STRING); PAL_CONSOLE_WRITE_STRING_LITERAL(PAL_OUTPUT_END_OF_LINE_STRING); } } } |
c ) I2C功能說明
QCA4020只需使用簡單幾個步驟就可以利用I2C對Slave設備做讀取或寫入。
這邊我來介紹讀取
QCA4020有兩組I2C,我們這邊使用I2C1,對應的GPIO為16、17,I2C core選擇QAPI_I2CM_INSTANCE_002_E。
qapi_I2CM_Open(QAPI_I2CM_INSTANCE_002_E, &h1); |
接下來將I2C速度及Slave位置設定好,再將要Read或Write的長度及Buffer設定好透過qapi_I2CM_Transfer()功能就可以完成I2C功能。
qapi_I2CM_Config_t config_humidity_temperature_addr = //I2C結構 { 100, /**< I2C bus speed in kHz. */ address, /**< 7-bit I2C slave address. */ 0, /**< SMBUS mode transfers. Set to TRUE for SMBUS mode. */ 0, /**< Maximum slave clock stretch in us that a slave might perform. */ 0, /**< Core Specific Configuration. Recommended 0. */ 0, /**< Core Specific Configuration. Recommended 0. */ }; desc.buffer = ®_val; desc.length = 1; desc.transferred = 0; desc.flags = QAPI_I2C_FLAG_START |QAPI_I2C_FLAG_READ | QAPI_I2C_FLAG_STOP; status = qapi_I2CM_Transfer (h1, &config_humidity_temperature_addr, &desc, 1,I2CM_Transfer_cb, NULL); //執行I2C傳輸,傳輸成功會呼I2CM_Transfer_cb |
做完I2C功能後,一定要將I2C關閉,這樣下次使用才不會有問題。
qapi_I2CM_Close(h1); |
d ) GPIO功能說明
QCA4020可以透過TLMM結構來指定QCA4020上GPIO的配置。這樣就可以很輕易的將GPIO配置為Input或Output,以及設定內部上拉或者下拉電阻。
typedef struct { uint32_t pin; /**< Physical pin number. */ uint32_t func; /**< Pin function select. */ qapi_GPIO_Direction_t dir; /**< Direction (input or output). */ qapi_GPIO_Pull_t pull; /**< Pull value. */ qapi_GPIO_Drive_t drive; /**< Drive strength. */ }qapi_TLMM_Config_t; |
QCA4020比較特別的地方是,每個GPIO 腳位都需要有各自的GPIO ID,所以編輯程式時候需要注意GPIO ID不可以共用,不然程式會有問題。
//每個GPIO要有各自的GPIO ID qapi_GPIO_ID_t gpio_id_green; qapi_GPIO_ID_t gpio_id_button_int; qapi_GPIO_ID_t gpio_id_pin_high; |
接下來我們用程式碼註解來說明如何將GPIO PIN 20設定為Output。
qapi_Status_t Initialize_Button_Led_GPIO(void) //LED初始化 { qapi_TLMM_Config_t tlmm_config; qapi_Status_t status = QAPI_ERROR;
tlmm_config.pin = 20; //Green是GPIO 20 tlmm_config.func = 0; tlmm_config.dir = QAPI_GPIO_OUTPUT_E; //設定GPIO為輸出模式 tlmm_config.pull = QAPI_GPIO_NO_PULL_E; tlmm_config.drive = QAPI_GPIO_16MA_E; status = qapi_TLMM_Get_Gpio_ID( &tlmm_config, &gpio_id_green); //每一個GPIO都需要有各自的GPIO ID if (status == QAPI_OK) { status = qapi_TLMM_Config_Gpio(gpio_id_green, &tlmm_config); //配置GPIO參數 if (status != QAPI_OK) { QCLI_Printf(qcli_button_handle, "GPIO_NUM_RED_LIGHT Fail to qapi_TLMM_Config_Gpio"); return QAPI_ERROR; } } else return QAPI_ERROR; return QAPI_OK; } |
e ) GPIO中斷功能說明
QCA4020的中斷設定非常方便,可以透過qapi_GPIOINT_Register_Interrupt函式,將需中斷的腳位以及觸發方式還有觸發後需要呼叫的副程式這幾點關鍵因素設定好,就可完成對中斷的設定。
qapi_Status_t qapi_GPIOINT_Register_Interrupt ( qapi_Instance_Handle_t *pH, uint32_t nGpio, qapi_GPIOINT_CB_t pfnCallback, qapi_GPIOINT_Callback_Data_t nData, qapi_GPIOINT_Trigger_e eTrigger, qapi_GPIOINT_Priority_e ePriority, qbool_t bNmi ); |
f ) AITg_humidity_temperature.c說明
Initialize_Humidity_Temperature_Demo()主要掃描I2C Slave設備,並將對應的I2C address透過UART顯示出來,Sensors_Humidity_Temperature_Get_Measured_Value()是透過ST HTS221的Datasheet將相關的資料讀取出來運算,這邊就不做說明 。
void Initialize_Humidity_Temperature_Demo(void) { qapi_Status_t status; qcli_humidity_temperature_handle = QCLI_Register_Command_Group(NULL,&humidity_temperature_cmd_group); if (qcli_humidity_temperature_handle) { QCLI_Printf(qcli_humidity_temperature_handle, "qcli_humidity_temperature_handle Registered\n"); } //I2C Scan Slave Address for (int address = 0x01; address <= 0x7f/*0x7f*/; address++) { //開啟I2C功能,我們這個範例是使用GPIO16、17所以需使用QAPI_I2CM_INSTANCE_002_E。 status = qapi_I2CM_Open(QAPI_I2CM_INSTANCE_002_E, &h1); if (status != QAPI_OK) { QCLI_Printf(qcli_humidity_temperature_handle, " ------ qapi_I2CM_Open fail ------\n"); } qurt_signal_init(&i2c_ready_signal); do { qapi_I2CM_Descriptor_t desc; uint8 reg_val; qapi_I2CM_Config_t config_humidity_temperature_addr = //I2C結構 { 100, /**< I2C bus speed in kHz. */ address, /**< 7-bit I2C slave address. */ 0, /**< SMBUS mode transfers. Set to TRUE for SMBUS mode. */ 0, /**< Maximum slave clock stretch in us that a slave might perform. */ 0, /**< Core Specific Configuration. Recommended 0. */ 0, /**< Core Specific Configuration. Recommended 0. */ }; desc.buffer = ®_val; desc.length = 1; desc.transferred = 0; desc.flags = QAPI_I2C_FLAG_START |QAPI_I2C_FLAG_READ | QAPI_I2C_FLAG_STOP; //執行I2C傳輸,傳輸成功會呼I2CM_Transfer_cb status = qapi_I2CM_Transfer (h1, &config_humidity_temperature_addr, &desc, 1,I2CM_Transfer_cb, NULL); if (status != QAPI_OK) { QCLI_Printf(qcli_humidity_temperature_handle, " --- qapi_I2CM_Transfer fail ---\n"); } qurt_signal_wait(&i2c_ready_signal, I2CM_READY_SIG_MASK, QURT_SIGNAL_ATTR_CLEAR_MASK); if (desc.flags == 0x0a) { QCLI_Printf(qcli_humidity_temperature_handle, "AITg I2C Slave address 0x%x\n",address); } }while(0); qurt_signal_destroy(&i2c_ready_signal); status = qapi_I2CM_Close(h1); //關閉I2C功能, if (status != QAPI_OK) { QCLI_Printf(qcli_humidity_temperature_handle, " ------ qapi_I2CM_Close fail ------\n"); } } } |
g ) AITg_button.c說明
我們會利用Button(S6)來點亮LED(Green)同時I2C讀取ST HTS221溫溼度,由於QCA4020 CDB開發版上的Button(S6)是由GPIO 10與GPIO 11組成(圖5),所以於程式馬上會看到GPIO 10與GOIO 11的設定。
(圖5) |
void Initialize_Button_Demo(void) //pal.c 呼叫的功能 { qcli_button_handle = QCLI_Register_Command_Group(NULL, &button_cmd_group); if (qcli_button_handle) { QCLI_Printf(qcli_button_handle, "Button Registered\n"); } if(Initialize_Button_Led_GPIO() != QAPI_OK) //將LED(Green) GPIO 20初始化 QCLI_Printf(qcli_button_handle, "Initialize_Button_Led_GPIO fail \n"); Button_Thread_Init(); //Button 線程初始化 if(Initialize_Button_Interrupt_GPIO() != QAPI_OK) //Button 中斷初始化 QCLI_Printf(qcli_button_handle, "Initialize_Button_Interrupt_GPIO fail \n"); }
qapi_Status_t Initialize_Button_Led_GPIO(void) //LED初始化 { //Create GPIO qapi_TLMM_Config_t tlmm_config; qapi_Status_t status = QAPI_ERROR;
tlmm_config.pin = GPIO_NUM_GREEN_LIGHT; //Green是GPIO 20 tlmm_config.func = 0; tlmm_config.dir = QAPI_GPIO_OUTPUT_E; //設定GPIO為輸出模式 tlmm_config.pull = QAPI_GPIO_NO_PULL_E; tlmm_config.drive = QAPI_GPIO_16MA_E; status = qapi_TLMM_Get_Gpio_ID( &tlmm_config, &gpio_id_green); //每一個GPIO都需要有各自的GPIO ID if (status == QAPI_OK) { status = qapi_TLMM_Config_Gpio(gpio_id_green, &tlmm_config); //配置GPIO參數 if (status != QAPI_OK) { QCLI_Printf(qcli_button_handle, "GPIO_NUM_RED_LIGHT Fail to qapi_TLMM_Config_Gpio"); return QAPI_ERROR; } } else return QAPI_ERROR; return QAPI_OK; }
void Button_Thread_Init(void) //Button 線程初始化 { qurt_thread_attr_t Thread_Attribte; qurt_thread_t Thread_Handle; qapi_Status_t Result; /* Start the main demo thread. */ qurt_thread_attr_init(&Thread_Attribte); qurt_thread_attr_set_name(&Thread_Attribte, "Button_Thread"); qurt_thread_attr_set_priority(&Thread_Attribte, 15); //線程權重 qurt_thread_attr_set_stack_size(&Thread_Attribte, 1024); //線程支援空間大小 Result = qurt_thread_create(&Thread_Handle, &Thread_Attribte, Button_Thread, NULL); //創建線程 if(Result != QAPI_OK) { QCLI_Printf(qcli_button_handle,"Failed to start Button_Thread_Init. \r\n"); } if (qurt_signal_init(&Button_Interrupt_signal) != QAPI_OK) { QCLI_Printf(qcli_button_handle,"Not able to initialize signal\n"); } }
void Button_Thread( void *data) { qapi_Status_t status = QAPI_ERROR; while(true) { qurt_signal_wait(&Button_Interrupt_signal, Button_INTR_EVENT, QURT_SIGNAL_ATTR_CLEAR_MASK); //等待Button Interrupt信號 if(qapi_TLMM_Read_Gpio(gpio_id_button_int, GPIO_NUM_BUTTON_PIN) == QAPI_GPIO_LOW_VALUE_E) { status = qapi_TLMM_Drive_Gpio(gpio_id_green, GPIO_NUM_GREEN_LIGHT, QAPI_GPIO_HIGH_VALUE_E); //點亮LED(Green) if (status != QAPI_OK) { QCLI_Printf(qcli_button_handle, "GPIO_NUM_RED_LIGHT qapi_TLMM_Drive_Gpio High Fail \n"); } if(Sensors_Humidity_Temperature_Get_Measured_Value() != QAPI_OK) //讀取溫溼度數值 QCLI_Printf(qcli_button_handle, " Sensors_Humidity_Temperature_Get_Measured_Value NO_OK\n"); } else { status = qapi_TLMM_Drive_Gpio(gpio_id_green, GPIO_NUM_GREEN_LIGHT, QAPI_GPIO_LOW_VALUE_E); //關閉LED(Green) if (status != QAPI_OK) { QCLI_Printf(qcli_button_handle, "GPIO_NUM_RED_LIGHT qapi_TLMM_Drive_Gpio low Fail \n"); } } qapi_Task_Delay(100*1000); //delay 100 ms 讓LED(Green)亮100ms } }
qapi_Status_t Initialize_Button_Interrupt_GPIO(void) //Button Interrupt初始化 { //Create GPIO Interrupt qapi_TLMM_Config_t tlmm_config; qapi_Status_t status = QAPI_ERROR; //GPIO 11腳位接地,使用GPIO 10來當開關按鈕 tlmm_config.pin = GPIO_NUM_BUTTON_HIGH; //GPIO 11 tlmm_config.func = 0; tlmm_config.dir = QAPI_GPIO_OUTPUT_E; //設定GPIO為輸出模式 tlmm_config.pull = QAPI_GPIO_NO_PULL_E; tlmm_config.drive = QAPI_GPIO_16MA_E; status = qapi_TLMM_Get_Gpio_ID( &tlmm_config, &gpio_id_pin_high); //每一個GPIO都需要有各自的GPIO ID if (status == QAPI_OK) { status = qapi_TLMM_Config_Gpio(gpio_id_pin_high, &tlmm_config); //配置GPIO參數 if (status != QAPI_OK) { QCLI_Printf(qcli_button_handle, "GPIO_NUM_BUTTON_LOW Fail to qapi_TLMM_Config_Gpio"); return QAPI_ERROR; } } else { QCLI_Printf(qcli_button_handle, "GPIO_NUM_BUTTON_HIGH qapi_TLMM_Get_Gpio_ID Fail"); return QAPI_ERROR; }
status = qapi_TLMM_Drive_Gpio(gpio_id_pin_high, GPIO_NUM_BUTTON_HIGH, QAPI_GPIO_LOW_VALUE_E); //設定GPIO 11為LOW
if (status != QAPI_OK) { QCLI_Printf(qcli_button_handle, "GPIO_NUM_BUTTON_LOW qapi_TLMM_Drive_Gpio low Fail \n"); }
tlmm_config.pin = GPIO_NUM_BUTTON_PIN; //GPIO 10 tlmm_config.func = 0; // Using the functionality tied to pin mux value tlmm_config.dir = QAPI_GPIO_INPUT_E; //設定GPIO為輸入模式 tlmm_config.pull = QAPI_GPIO_PULL_UP_E; //設定GPIO上拉 tlmm_config.drive = QAPI_GPIO_2MA_E; status = qapi_TLMM_Get_Gpio_ID(&tlmm_config, &gpio_id_button_int); //每一個GPIO都需要有各自的GPIO ID if (status == QAPI_OK) { status = qapi_TLMM_Config_Gpio(gpio_id_button_int, &tlmm_config); if (status != QAPI_OK) { QCLI_Printf(qcli_button_handle, "GPIO_NUM_BUTTON_PIN Fail to qapi_TLMM_Config_Gpio \n"); return QAPI_ERROR; } } else { QCLI_Printf(qcli_button_handle, "GPIO_NUM_BUTTON_PIN qapi_TLMM_Get_Gpio_ID Fail"); return QAPI_ERROR; } //設定GPIO 10雙邊緣觸發ButtonIntr()副程式 if ( qapi_GPIOINT_Register_Interrupt(&Button_Int_hdl, GPIO_NUM_BUTTON_PIN, (qapi_GPIOINT_CB_t)ButtonIntr, 0, QAPI_GPIOINT_TRIGGER_EDGE_DUAL_E, QAPI_GPIOINT_PRIO_MEDIUM_E, false) != QAPI_OK) { QCLI_Printf(qcli_button_handle,"Error in GPIO_NUM_BUTTON_PIN Registering Interrupt! \n"); return QAPI_ERROR; } QCLI_Printf(qcli_button_handle,"Initialize_Button_Interrupt_GPIO Ok! \n"); return QAPI_OK; }
//當GPIO 10被觸發時,發送Button Interrupt信號 static void ButtonIntr(qapi_GPIOINT_Callback_Data_t pin) { qurt_signal_set(&Button_Interrupt_signal, Button_INTR_EVENT); } |
結語
看完這次的實例後,相信大家對GPIO、I2C應用有了基本的瞭解,在開發產品時也能更快上手,如果忘記如何編譯與燒錄請回顧上一篇Qualcomm QCA4020 CDB 環境建置快速上手,其他更多的I/O功能,就等大家去探索了,我們下回見。
評論