完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 城东 于 2016-12-10 11:38 编辑 关于user_init的具体内容和一个简单的程序编译下载等流程请看本人之前的文章:https://bbs.elecfans.com/forum.ph ... &tid=1098080&extra= 入口函数的第一句话就是调用串口打印函数:os_printf("software version:%sn", SOFTWARE_VERSION);这里能够在串口中看到相应的信息那就说明在前面的系统初始化代码中一定已经包含了串口初始化的代码。我们看看这个函数,在source insight里可以看到有两个文件定义了这个函数: 至于最终需要的是哪个文件,我们在esp_common.h文件中看到这样的包含语句: #include "et_types.h" #include "c_types.h" #include "esp_libc.h" #include "esp_misc.h" #include "esp_wifi.h" #include "esp_softap.h" #include "esp_sta.h" #include "esp_system.h" #include "esp_timer.h" #include "esp_ssc.h" #include "esp_spiffs.h" #include "esp8266/esp8266.h" #include "smartconfig.h" #include "spi_flash.h" 说明这里使用的文件是esp_libc.h,那os_printf函数的定义如下: /* NOTE: don't use printf_opt in irq handler, for test */ #define os_printf(fmt, ...) do { static const char flash_str[] ICACHE_RODATA_ATTR STORE_ATTR = fmt; printf(flash_str, ##__VA_ARGS__); } while(0) 其中ICACHE_RODATA_ATTR定义如下: #define ICACHE_RODATA_ATTR __attribute__((section(".irom.text"))) 那么 这里flash_str这个数组就只是一个存储在.irom.text(代码区)的一个特殊数组,那么这个函数的第一句话就是把打印格式本分到.irom.text区,最终的flash_str=fmt, 有意思的是printf(flash_str, ##__VA_ARGS__); 这句话,flash_str=fmt,所以printf(flash_str, ##__VA_ARGS__);就是printf(fmt, ##__VA_ARGS__);而__VA_ARGS__= ...,关于__VA_ARGS__请看http://blog.sina.com.cn/s/blog_661314940100qmfg.html 那到这里就可以知道 os_printf(fmt, ...)等同于 printf(fmt, ...),只是多了一个备份格式的操作而已,另外那既然printf函数是打印到串口的,那么必定要重写int fputc(int ch, FILE *f)函数,但是在source insight中怎么到找不到这个函数的定义,那么 fputc函数只能够藏在一个地方:shareSDKlib下面的.a文件中,这个是唯一一个有可能再次有代码的地方,逼不得已呀,这里直接把shareSDKlib里的所有.a文件的API(函数)给打印出来,如下: esp8266@esp8266-VirtualBox:~$ cd ~/Share/SDK/lib esp8266@esp8266-VirtualBox:~/Share/SDK/lib$ ls libairkiss.a libfac.a libmesh.a libpp.a libwpa.a libcirom.a libfreertos.a libminic.a libpwm.a libwps.a libcrypto.a libgcc.a libmirom.a libsmartconfig.a libespconn.a libjson.a libnet80211.a libspiffs.a libespnow.a liblwip.a libnopoll.a libssc.a libetilink.a libmain.a libphy.a libssl.a esp8266@esp8266-VirtualBox:~/Share/SDK/lib$ nm *.a libairkiss.a: airkiss.o: 0000060c T airkiss_change_channel 00000000 t __airkiss_crc8 00000598 T airkiss_get_result 00000028 T airkiss_init 0000007c T airkiss_recv 00000020 T airkiss_version 00000018 r CRC8_ARRAY ................................................................................. 这里的打印信息太多,保存到如下文件:
库文件函数说明.zip
(58.17 KB, 下载次数: 19
)
这个压缩包里面的《库文件函数说明.txt》就是所有的库函数信息 这里我们可以查到fputc函数: lib_a-fputc.o: 00000034 T fputc 00000000 T _fputc_r U _impure_ptr U _putc_r U __sinit 说明fputc函数确实被定义了,只不过被封了起来而已! 继续分析user_init函数: if(get_fac_norm_mode(&result) != SPI_FLASH_RESULT_OK) { os_printf("get_fac_norm_mode error, NORMAL moden"); } if(result == FAC_MODE) { os_printf("run in factory moden"); uart_init_new_uart1(BIT_RATE_115200); UART_SetPrintPort(UART1); uart_init_new(BIT_RATE_115200, result); return; } os_printf("run in normal moden"); 这里先从flash获取现在的运行模型,如果运行模型是出厂模式,result == FAC_MODE那么到这里之后设置完波特率就会退出程序。下来: //if define IR_DEMO, ir rx or tx test #ifdef IR_DEMO struct station_config config; memset(&config, 0, sizeof(config)); wifi_set_opmode(STATION_MODE); wifi_station_set_config_current(&config); //if define IR_RX, ir rx test #ifdef IR_RX ir_rx_init(); //ir tx test #else ir_tx_init(0); xTaskCreate(ir_tx_key, "ir_tx_key", 256, NULL, 2, NULL); #endif #else 如果当前的例程是IR_DEMO红外例程,将调用上面的程序,这里首先设置WiFi为station模型,然后如果定义IR_RX 红外接收模型,这里将会调用 ir_tx_init函数来初始化红外接收功能,如果没有定义为接受模型就会调用 ir_tx_init函数来初始化红外发送功能,不管是接收还是发送都会调用xTaskCreate来创建一个任务,这里我们分析上面提到的这三个函数, ir_rx_init: ring_buf_init(&ir_rx_ring_buf, ir_rx_buf, sizeof(ir_rx_buf)); ir_rx_gpio_init(); xTaskCreate(ir_rx_task, "ir_rx_task", 256, NULL, 2, NULL); 函数ring_buf_init初始化ir_rx_ring_buf这个缓存结构体,如下: typedef struct { et_uint8 * p_o; /**< Original pointer */ et_uint8 * volatile p_r; /**< Read pointer */ et_uint8 * volatile p_w; /**< Write pointer */ volatile et_int32 fill_cnt; /**< Number of filled slots */ et_int32 size; /**< Buffer size */ }ring_buf_t; 函数ir_rx_gpio_init就是初始化接收红外的关键: LOCAL void ir_rx_gpio_init() { //Select pin as gpio PIN_FUNC_SELECT(IR_GPIO_IN_MUX, IR_GPIO_IN_FUNC); //set GPIO as input GPIO_DIS_OUTPUT(IR_GPIO_IN_NUM); PIN_PULLUP_DIS(IR_GPIO_IN_MUX); //clear interrupt status GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(IR_GPIO_IN_NUM)); //set negitive edge interrupt gpio_pin_intr_state_set(IR_GPIO_IN_NUM, GPIO_PIN_INTR_NEGEDGE); //register interrupt handler ETS_GPIO_INTR_ATTACH(ir_rx_intr_handler, NULL); //enable gpio interrupt ETS_GPIO_INTR_ENABLE(); } 其中LOCAL代表static,这个函数的功能是初始化gpio5这个IO口,并且使能该IO口的中断。函数ir_rx_init的最后内容是创建一个任务名是"ir_rx_task"回调函数是ir_rx_task,任务缓存区是256,任务优先级是2,这里让我们看看这个任务回调函数: void ir_rx_task(void *pvParameters) { et_uint8 ir_data; while(1) { if(ir_rx_ring_buf.fill_cnt > 0) { ring_buf_get(&ir_rx_ring_buf, &ir_data, 1); os_printf("ir_data = %02xhn", ir_data); } delay_ms(500); } } 注意这里的任务回调函数是一个死循环,不会退出,那任务的调度就交给了操作系统,这里的回调函数的主要内容就是判断是否红外有接收到数据,然后通过串口打印出来,但是至于这个接收的数据的填充工作就应该在gpio5这个管脚中断上了, 这里我们看看这个管脚的中断: LOCAL void ir_rx_intr_handler(void *para) { et_int32 status; status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); if((status >> IR_GPIO_IN_NUM) & BIT0) { ir_intr_handler(); } else { os_printf("gpio num mismachedn"); GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, status); } }其中 ir_intr_handler函数经历了一些列的时序判断调用了这个语句: ring_buf_put(&ir_rx_ring_buf, (ir_cmd >> 16) & 0xff); 这里吧数据填充到ir_rx_ring_buf数组中 再回到user_init函数继续往下分析,如果不是红外接收模式,那就是红外发送模式,红外发送模式下调用ir_tx_init函数来初始化红外发送: void ir_tx_init(et_uint8 req) { hw_timer_init(req); hw_timer_set_func(ir_tx_handler); }因为产生红外通许波形需要一个定时器,所以这里初始化一个定时器,这里传送的参数是0,所以这里的定时器初始化如下:RTC_REG_WRITE(FRC1_CTRL_ADDRESS, FRC1_ENABLE_TIMER | DIVDED_BY_16 | TM_EDGE_INT);没有自动重装载的功能,所以定时器中断的时候必须要重装定时器数据,这也达到了产生不一样的占空比的功能。这里还注册了定时器中断处理函数ir_tx_handler,在这个函数主要的功能就是参数红外波形,这个叫做NEC时序的东西。因为红外发送口在GPIO14,和很多模块共用,所以这里我们看关键的一句WRITE_PERI_REG(IR_GPIO_OUT_MUX, (READ_PERI_REG(IR_GPIO_OUT_MUX)&0xfffffe0f)| (0x1<<4) );(在gen_carrier_clk函数中),这里我们知道他操作了红外发射管脚。红外发射程序的最后一步是设置一个任务名为"ir_tx_key",回调函数是ir_tx_key,缓存也是256,优先等级为2. 上面所有关于红外的内容本例程都没有使用到,这里我们继续分析要使用的例程,接下来的内容如下: key_gpio_t key; struct station_config config; struct ip_info info; // show logo user_show_logo(); //通过OLED显示logo 这里除了定义唯一有用的语句就是 user_show_logo,该函数如下: user_show_logo() { extern et_uchar BMP1[]; et_uint32 len = 1024; // BMP1 member i2c_master_gpio_init(); // I2C init OLED_init(); // OLED init OLED_clear(); // show logo OLED_show_bmp(0, 0, 128, 8, BMP1, len); } 这里初始化了OLED_init,然后清屏,最后显示logoOLED_show_bmp(0, 0, 128, 8, BMP1, len); |
|
相关推荐
4 个讨论
|
|
接下来是: if (RETURN_OK != user_get_work_mode(&work_mode)) { os_printf("get work mode fail !!!n"); return; }这里调用了 user_get_work_mode函数,其实这个函数就是判断拨码开关的状态,如下: user_get_work_mode(et_uint32 *mode) { et_uint32 adc = system_adc_read(); if (adc > 1024) { os_printf("The adc value=%u is invalid !!!n", adc); return RETURN_ERR; } #ifdef USER_PRINT_DEBUG os_printf("get adc value=%u is success !!!n", adc); #endif // ADC turn into work mode if(adc < 100) { *mode = WORK_MODE_DEFAULT; } else if (adc < 350) { *mode = WORK_MODE_AUDIO; } else if(adc < 550) { *mode = WORK_MODE_RGB; } else if(adc < 750) { *mode = WORK_MODE_BAROMETRIC; } else if(adc < 1000) { *mode = WORK_MODE_OLED; } else { *mode = WORK_MODE_BUTT; } return RETURN_OK; }这里结合下面的原理图就知道上面的含义了:当ADC的值在750到1000之间的时候工作在OLED的模式: 下来的内容是初始化工作模式,比如工作在OLED模式就初始化OLED等: if (RETURN_OK != user_init_work_mode(work_mode, result)) { os_printf("init work mode fail !!!n"); return; }这里看看user_init_work_mode函数: user_init_work_mode(et_uint32 mode, et_uchar fac_norm_mode) { if (WORK_MODE_BUTT <= mode) { os_printf("The work mode=%u is invalid !!!n", mode); return RETURN_ERR; } //#ifdef USER_PRINT_DEBUG os_printf("get work mode=%s is success !!!n", user_get_mode_str(mode)); //#endif switch (mode) { case WORK_MODE_DEFAULT: DHT11_init(); break; case WORK_MODE_AUDIO: DHT11_init(); audio_init(); break; case WORK_MODE_RGB: RGB_light_init(); // RGB init DHT11_init(); break; case WORK_MODE_BAROMETRIC: i2c_master_gpio_init(); // BAROMETRIC init DHT11_init(); // temperature init break; case WORK_MODE_OLED: i2c_master_gpio_init(); // I2C init OLED_init(); // OLED init OLED_clear(); DHT11_init(); OLED_show_chn(0, 3, 16); OLED_show_chn(16, 3, 17); OLED_show_chn(32, 3, 18); OLED_show_chn(48, 3, 19); OLED_show_chn(64, 3, 20); OLED_show_chn(80, 3, 21); OLED_show_chn(96, 3, 22); OLED_show_chn(112, 3, 23); break; default: break; } return RETURN_OK; }接下来的内容就开始涉及wifi操作了: //wifi event handle wifi_set_event_handler_cb(et_wifi_event_cb); 这里初始化wifi事件回调函数et_wifi_event_cb,有关所有的wifi事件的相应函数都在这里,比如: void et_wifi_event_cb(System_Event_t *event) { switch(event->event_id) { case EVENT_STAMODE_SCAN_DONE://ESP8266 station finish scanning AP break; case EVENT_STAMODE_CONNECTED://ESP8266 station connected to AP os_printf("et connect to ssid %s, channel %dn", event->event_info.connected.ssid, event->event_info.connected.channel); break; 这里的case EVENT_STAMODE_CONNECTED分支就是开发板连接到wifi的时候回调的函数。接下来: memset(&key, 0, sizeof(key_gpio_t)); key.key_gpio_pin = AIRKISS_KEY_IO_PIN; key.key_num = AIRKISS_KEY_IO_NUM; airkiss_key_init(&key); wifi_set_opmode(STATION_MODE); wifi_reconnect_start_flag = 0; xTaskCreate(airkiss_key_poll_task, "smartconfig_task", 256, NULL, 2, NULL); 这里初始化了AIRKISS_KEY这个按键(GPIO0),但是这个按键在原理图上叫做BOOT,这里应该写错名字了,反正原理图和开发板和程序有点出入,然后这里设置WiFi模式为sattion模式,最后设置一个按键监视的任务airkiss_key_poll_task,这个任务监视按键airkiss的状态,应该是根据这个按键的状态做出相应的响应。按照实验的现象这里应该就是连接wifi等,这里目前不深究。接下来: wifi_led_init(); memset(&config, 0, sizeof(struct station_config)); if(wifi_station_get_config_default(&config) == true) { os_printf("ssid=%sn", config.ssid); wifi_station_set_config_current(&config); //for static ip set /*wifi_station_dhcpc_stop(); IP4_ADDR(&info.ip, 192, 168, 1, 43); IP4_ADDR(&info.gw, 192, 168, 1, 1); IP4_ADDR(&info.netmask, 255, 255, 255, 0); wifi_set_ip_info(STATION_IF, &info);*/ } 这里配置WiFi指示灯之后进行了WiFi的配置config,这里好像没有进行什么实质性的工作,然后: os_timer_disarm(&test_timer); os_timer_setfn(&test_timer, (os_timer_func_t *)user_esp_platform_check_ip, NULL); os_timer_arm(&test_timer, 1000, 0); 这里设置了一个定时器test_timer,先要把这个定时器卸载掉,然后初始化定时器的回调函数user_esp_platform_check_ip,最后再装载这个定时器,user_esp_platform_check_ip函数的主要内容是打印wifi目前的状态信息。还有一句比较重要的语句:xTaskCreate(et_user_main, "et_user_main", 1024, NULL, 2, NULL);当配置上IP之后将使能这个任务 到了这里基本上user_init函数分析完毕。 这里为了方便再次上传代码:
share.zip
(8.61 MB, 下载次数: 29
)
|
|
|
|
|
|
只有小组成员才能发言,加入小组>>
[小e体验板] 【小体积大智慧】关于体验板的技术问题,看这一篇就够了
2855 浏览 4 评论
【小e1开发板试用体验】分析程序架构四~/Share/SDK/makefile分析
3613 浏览 1 评论
6706 浏览 19 评论
29081 浏览 59 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-28 01:00 , Processed in 1.543578 second(s), Total 78, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号