• 易迪拓培训,专注于微波、射频、天线设计工程师的培养
首页 > 无线通信 > 技术讨论 > 6、固件源码详解(二)

6、固件源码详解(二)

录入:edatop.com     点击:

      这次接着上一次的贴子继续,上次先主要分析了固件源码的所以相关文件内容和整体框架,以及一个高效的程序编辑器的安装,接下来就使用这个编辑器去查看源代码并逐步去修改,下面直接进入话题:



1、找到main函数的初始化部分。

       程序的入口就是init,从这里就可以一步一步找到硬件的初始化流程,遇到函数的调用了我们就进去看一看究竟。在编辑器上面可以输入关键词搜索如下图所示:


2、串口启动信息。


    我们给板子烧录完固件之后,打开我们的串口助手,输入波特率74880,打开串口。之后将跳线帽短接到OLED模式,拨码开关也同步到OLED,然后打开小e的电源开关,注意观察串口助手的输出信息:


      最开始一上电就会出输出一堆信息,可能是boot启动信息,这个无关紧要我们不必太关注,暂时先找到我们能到读懂的能够在程序中看到的信息,如果后期玩的差不多了所有流程都了解透了再来深入也不迟,现在去理解就是给自己添麻烦了!上面的红框内信息在程序中是可以一一对应到的,如下图红框内容:


①  os_printf("nowSDK version:%s\n", system_get_sdk_version());


    程序运行之后首先打印出的就是这条信息,内容是和串口助手的第一条信息一模一样,后面调用了system_get_sdk_version()函数打印出SDK的版本。


②  os_printf("runin normal mode\n");


    这条信息和第一条信息直接隔了两个if语句,两条if语句判断完之后打印此消息,串口助手可以看到是紧接着第一条信息的。


3、模式检测。

                    然后我们看一下中间的那两个if语句什么作用?

   

    第一个判断的从函数名字来看模糊的意思什么模式结果不匹配的话打印出一条信息,但是我们没有看到打印,所以这条语句没有执行。我们就进入get_fac_norm_mode()函数看一下:

SpiFlashOpResult get_fac_norm_mode(et_uchar*result)

{

       SpiFlashOpResult rc=SPI_FLASH_RESULT_OK;

       et_uchar fac_norm_mode[FAC_NORM_MODE_LEN]={0};

      

       rc = spi_flash_read(MODE_INFO_FLASH_SECTOR *SPI_FLASH_SEC_SIZE, (et_uint32 *)fac_norm_mode, sizeof(fac_norm_mode));

       if(rc != SPI_FLASH_RESULT_OK)

       {

              *result = NORM_MODE;

              return rc;

       }

       if(!strncmp(fac_norm_mode, FACTORY, strlen(FACTORY)))

       {

              *result = FAC_MODE;

       }

       else if(!strncmp(fac_norm_mode, NORMAL, strlen(NORMAL)))

       {

              *result = NORM_MODE;

       }

       else

       {

              *result = NORM_MODE;

              printf("mode error, default normal mode,fac_norm_mode = %s\n", fac_norm_mode);

       }

      

       return rc;

}

    从这个内容大致可以知道应该是为了读出SPI_Flash的结果从而判断出目前处于什么模式。一种是正常模式——normal,另一种是工厂模式——factory,并将检测到的结果赋给指针*result。至于怎么检测的,应该是有相应的几个数字去匹配,先不去研究。

然后我们退出这个函数看看第二个if语句内容:

if(result == FAC_MODE)

       {

              os_printf("run in factory mode\n");

              uart_init_new_uart1(BIT_RATE_115200);

              UART_SetPrintPort(UART1);

              uart_init_new(BIT_RATE_9600, result);

              return;

       }

    这就很明显了哈,如果第一个if语句得出是工厂模式——factory,那么正好进入这个函数内部,打印信息:os_printf("runin factory mode\n")表示进入工厂模式;然后设置新的串口1波特率,最后return退出user_init(),之后就应该进入其他程序了。但是如果我们在开发板上没有任何操作的话并没有进入工厂模式,而是进入了正常模式,所以下一步就有了打印出os_printf("runin normal mode\n");的信息,这也就是串口助手中的第二条消息。到这里就算是知道了模式,程序还是在user_init()函数里面走着呢,那我们就继续看!


4、显示logo。

    接下来就走到了user_show_logo()这一步,很明显这就是小e上电后OLED显示的“开发快“的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);

       delay_s(4);

       OLED_clear();

}

    里面可以看出都是一些图片的设置信息,包括图片位置、大小、位图的一些指令,还有一些OLED显示屏的控制指令:启动初始、清屏延时之类的。以后我们可以修改一下这里的信息看看效果如何。




5、正常模式下的工作模式选择和初始化。

首先还是判断在正常模式下处于什么工作模式:

if (RETURN_OK !=user_get_work_mode(&work_mode))

       {

              os_printf("get work mode fail !\n");

              return;

       }


看到调用了另外一个函数user_get_work_mode()我们就进去看看:


       首先说明一下源码虽然是开源的,但是程序中有部分函数是原厂封装到ESP8266芯片内部的,他们只给了程序员相关的头文件和函数接口,只能拿来用而我们是完全看不到函数里面的内容的!就像上面红框中system_adc_read()函数,在编辑器里面是进入不了函数内部的,只能看到其定义!

       用官方的说法就是:  原厂就是指设计这款芯片的厂家,设计芯片的公司不但会设计芯片的硬件逻辑,为了让使用这颗芯片使用的人员方便使用,往往还会给出很多硬件相关的通用软件接口,比如说他的adc,他把adc集成在了处理器上,对adc的操作就是对adc相应寄存器进行操作,就象51一样,原厂的软件工程师把对adc寄存器操作的代码写在一个函数里,并以库的形式给出,用户只需要包含这个库,并调用就可以了。 易通星云的硬件工程师拿到8266这颗芯片,设计了adc的外围电路,也就是拨码部分,他们根据外围电路的情况,调用原厂的adc函数,读出adc的值,并根据数值做模式区分,即小e开发板的几种模式切换!  



         在这user_get_work_mode()这个函数里面我们可以看到首先是定义了一个adc变量,调用system_adc_read()函数将值赋给adc。后面开始逐一判断adc的数值大小,每一个范围内的数值代表一种模式,分别是:DEFAULT模式、AUDIO模式、RGB模式、BAROMETRIC模式、OLED模式、BUTT模式。如果adc的值在正确范围内,该函数就返回RETURN_OK;若大于1024,则返回RETURN_ERR,也就是有误,同时打印信息“The adc value=%u is invalid!”提示变量adc数值无效!退到user_init()函数可以看到第一个if语句就在判断那个函数返回值是不是RETURN_OK,如果不是说明返回的是RETURN_ERR,工作模式有误“get work mode fail !”退出整个user_init函数!

    下一个if语句判断工作模式的初始化,如果上一步正确选择了工作模式,在这里面检测是哪种模式并进行初始化。同样调用了另外一个函数user_init_work_mode(),通过配对上一个函数的指针来初始化相应的工作模式内容如下:


    和上面一个if的功能一样,初始化失败的话就是模式没有选择正确,打印相关的信息:初始化失败“initwork mode fail !”退出整个user_init函数!


    看到这一点就想到了硬件上如何改变工作模式的,小e开发板上面有一个4位一体的拨码开关,打到不同的档位就是选择了不同的工作模式。程序是怎么知道是哪种呢?我们看到程序里面通过adc的数值范围不同判断的,一看到adc我就想到了单片机经常用到的AD和DA转换,再结合这部分的电路原理图就恍然大悟:拨码开关打到不同的档位,经过不同数量的电阻分压,每个线端的电压值就不一样,主控芯片检测这几个引脚端子电压就知道选择的是哪种模式了!


       到这里我才真正的明白了拨码开关原来是这么用的,还有这部分的程序是如何写的!可以说我通过这个开发板get到了一个新的技巧,真的可以说是公司一月学到的东西是学校一年的啊,如果我不深入的看一下马虎而过,我估计在我工作之前也不会知道这个原理!


                 当程序走到这一步的时候串口就会打印出选择的模式信息和相关数据,如下图所示:


    上面的两条信息我们可以看到adc的数值这时是882,属于adc<1000的范围,因此是属于OLED模式,而且上面的第二条信息也显示“获得OLED模式成功”,突然想到上次我烧录自己编译好的固件现象:OLED屏只显示开发快的logo之后就没有“开发从未如此简单”的字样了,刚才看了一下当时的adc数值是在<750的范围内的,属于BAROMETRIC——大气压模式,再低头看看我的拨码开关,哈哈,打错位置了,拨在了BAROMETRIC模式下,怪不得OLED不显示呢,当时是怎么也想不明白,现在想想还很好笑呢!


6、WiFi指示灯的模式。

程序接下来就是WiFi指示灯的显示状况了:

xTaskCreate(user_wifi_led_control,"wifi_led_control", 256, NULL, 2, NULL);


这里又调用了一个函数user_wifi_led_control(),程序内容如下图:


首先还是打印信息“wifiled thread will be start”,这个和串口信息是对上的:


之后是设置一些WiFi指示灯的引脚输出,判断WiFi_led的模式,有常亮、常灭、闪烁模式。


7、 WiFi工作模式状态。

wifi_set_event_handler_cb(et_wifi_event_cb);


调用了et_wifi_event_cb()函数:


     在switch里面有不同的工作环境分别对应ESP8266不同的状态:扫描AP状态、连接AP状态、断开AP状态、soft-AP状态等。

         WiFi连接成功后就工作在AP状态,WiFi指示灯长亮,同时打印WiFi相关信息:os_printf("et connect tossid %s, channel %d\n", event->event_info.connected.ssid,event->event_info.connected.channel);

              

WiFi断开,ESP8266就断开AP的状态,LED开始闪烁。之后还有判断是否是重启的情况。


    综合来说,这个函数的内容就是来处理ESP8266在不同模式下进行一些不同的其他操作,情况比较复杂,程序里面还参杂着好多其他情况,看起来比较费劲。




8、airkiss按键的初始化。


airkiss_key_init(&key);

    这里面就是对WiFi配置按键的初始化部分,设置按键的GPIO口味输入检测按键的按下状态,比如长按或者短按,都会在后面的程序里面判断到。


9、WiFi配置过程。

xTaskCreate(airkiss_key_poll_task,"smartconfig_task", 256, NULL, 2, NULL);


    当程序走到这一步的时候,就是你长按airkiss按键之后的情况,xTaskCreate函数调用airkiss按键的分派任务检测按键状态:



     当该任务检测到你的airkiss按键长按之后悔打印一下消息:os_printf("begin toairkiss\n");串口助手也会看到,说明已经开始配置WiFi了:

程序中设置了user_main函数的进程停止:

to_stop_app = 1;    //inairkiss mode, stop et_user_main thread

之后又有了这么一个函数,开始了airkiss  start的进程:

smartconfig_start(smartconfig_done);   //airkiss start


    我看到这里其实就有点不知道怎么回事了,对与几个函数进程的停止与运行时如何切换的完全不懂,更不知道这样的代码如何去写,只是懵懵懂懂的能勉强看下去,也只能这样硬着头皮继续看了。函数又调用了smartconfig_done()开始了airkiss  start进程,那就进去看看函数:



    这部分代码我是看不明白,反正还是WiFi环境的配置信息,不过中间有这么一句代码算是结束了这个函数的运行返回到上面一步的函数中去:

smartconfig_stop();

    这句代码从名字看知道是结束了smartconfig_done的进程,算是又跳到了airkiss_key_poll_task()的函数中去。不过这句代码无法查看,和之前说过的一样,是属于原厂在芯片内部封装好的函数和库,只提供了头文件和接口,拿来就直接调用了,所以在这里无法查看。

然后设置WiFiled延时为500ms:user_set_wifi_led_delay(500)

紧接着就是OLED的显示内容了,此时是显示“网络配置中…”。

    再接着后面有调用了另一个函数~~~~(天呐,我要疯了)user_esp_platform_check_ip();

这个函数名大致意思我猜测是“核对用户WiFi ip信息”的,通过里面的一个函数可以确定是获取到ip:   wifi_get_ip_info(STATION_IF, &ipconfig);

然后判断成功获取到ip之后LED长亮,表示WiFi配置成功,同时设置user_main_start_flag()函数的标志位,进而去回调到该函数,一旦配对成功,就开始执行user_main()函数,即用户主函数!反之,如果连接不成功,就会设置air_kiss_start_flag标志,重新回去执行网络配置函数直至配置成功!


WiFi连接成功之后,显示消息os_printf("endairkiss\n");

OLED刷新信息“网络配置完成”!


User_init()执行到最后核对IP:user_esp_platform_check_ip();

核对成功后就进入用户主程序user_main();


       到这里主函数初始化已经完成,顺利进入用户函数!中间的程序分析有的地方我可能搞迷糊了,真的是绕着绕着就把自己给绕进去了。全部源码还没完全熟悉,在此我也只是把我目前的认识分享一下,太累了,深夜两点了已经,肚子已经饿了,但是我还是选择睡觉吧!过几天在进一步学习,能学到多少就学多少!




本帖Word版教程:小e源码详解(二).rar(521.65 KB, 下载次数: 28)

2016-2-4 02:04 上传

点击文件名下载附件




4中工作模式判断.png

airkiss_key_poll_task函数.png(57.53 KB, 下载次数: 2)

下载附件 保存到相册

2016-2-4 02:03 上传

airkiss_key_poll_task函数.png

show_logo().png(44.83 KB, 下载次数: 2)

下载附件 保存到相册

2016-2-4 02:03 上传

show_logo().png

user_esp_platform_check_ip函数.png(53.37 KB, 下载次数: 2)

下载附件 保存到相册

2016-2-4 02:03 上传

user_esp_platform_check_ip函数.png

串口信息按下airkiss按键后.png(29.78 KB, 下载次数: 2)

下载附件 保存到相册

2016-2-4 02:04 上传

串口信息按下airkiss按键后.png

找到user_init.png(82.04 KB, 下载次数: 2)

下载附件 保存到相册

2016-2-4 02:04 上传

找到user_init.png

非常好啊

非常详细,这个是福音来的。 继续加油哦。

半个月前的最近该更新了

我到现在单片机都没有入门,不知这个是否详细

还是先学习单片机吧,买个开发板先玩着,玩熟练了接触其他模块,之后再玩其他的东西!

解读的很到位

经历过之后感觉还是不深入的,嵌入式相关的还没有开始入门

谢谢分享  ...很详细,赞一个 !

你好,请问一下wifi_set_event_handler_cb(et_wifi_event_cb);这个函数是怎么把et_wifi_event_cb(System_Event_t *event)中的event参数传递进来的?

上一篇:能不能给自己随身携带的物品如钥匙串和钱包等设计一个跟踪模块或标签,让手机可以随时定位?
下一篇:Zigbee是否真的穷途末路?NB-IoT是否能一统江湖?

手机天线设计培训教程详情>>

手机天线设计培训教程 国内最全面、系统、专业的手机天线设计培训课程,没有之一;是您学习手机天线设计的最佳选择...【More..

射频和天线工程师培训课程详情>>

  网站地图