<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ucosii在stm32上的移植詳解3

          ucosii在stm32上的移植詳解3

          作者: 時(shí)間:2016-11-09 來源:網(wǎng)絡(luò) 收藏
          移植詳解1和2中主要講了移植需要用到的基礎(chǔ)知識(shí),本文則對(duì)具體的移植過程進(jìn)行介紹。

          首先從micrium網(wǎng)站上下載官方移植版本(編譯器使用ARM/Keil的,V2.86版本,V2.85有問題)。
          下載地址:http://micrium.com/page/downloads/ports/st/stm32
          解壓縮后得到如下文件夾和文件:
          Micrium
          AppNotes
          Licensing
          Software
          ReadMe.pdf

          本文引用地址:http://www.ex-cimer.com/article/201611/318070.htm

          AppNotes包含ucosii移植說明文件。這兩個(gè)文件中我們僅需關(guān)心MicriumAppNotesAN1xxx-RTOSAN1018-uCOS-II-Cortex-M3AN-1018.pdf。因?yàn)檫@個(gè)文件對(duì)ucosii在CM3內(nèi)核移植過程中需要修改的代碼進(jìn)行了說明。
          Licensing包含ucosii使用許可證。
          Software下有好幾個(gè)文件夾,在本文的移植中僅需關(guān)心uCOS-II即可。
          CPU: stm32標(biāo)準(zhǔn)外設(shè)庫
          EvalBoards: micrium官方評(píng)估板相關(guān)代碼
          uc-CPU: 基于micrium官方評(píng)估板的ucosii移植代碼
          uC-LCD:micrium官方評(píng)估板LCD驅(qū)動(dòng)代碼
          uc-LIB: micrium官方的一個(gè)庫代碼
          uCOS-II: ucosii源代碼
          uC-Probe: 和uC-Probe相關(guān)代碼
          ReadMe.pdf就不說了。

          好了,官方的東西介紹完了,該我們自己建立工程著手移植了。關(guān)于建立工程,并使用stm32標(biāo)準(zhǔn)外設(shè)庫在我之前的文章《stm32標(biāo)準(zhǔn)外設(shè)庫使用詳解》已有介紹,這里請(qǐng)大家下載其中模板代碼(http://download.csdn.net/source/3448543),本文的移植是基于這個(gè)工程的。
          建立文件夾templatesrcucosii, templatesrcucosiisrc, templatesrcucosiiport;
          把MicriumSoftwareuCOS-IISource下的文件拷貝至templatesrcucosiisrc;
          把MicriumSoftwareuCOS-IIPortsARM-Cortex-M3GenericRealView下的文件拷貝至

          templatesrcucosiiport;
          ucosiisrc下的代碼是ucosii中無需修改部分,ucosiiport下的代碼是移植時(shí)需要修改的。為防止對(duì)源碼的誤改動(dòng)造成移植失敗,可以把ucosiisrc下的代碼文件設(shè)為只讀。
          這里根據(jù)AN-1018.pdf和移植詳解1、2中介紹的移植基礎(chǔ)知識(shí),對(duì)ucosiiport下的代碼解釋一下。

          os_cpu.h

          #ifdef OS_CPU_GLOBALS
          #define OS_CPU_EXT
          #else
          #define OS_CPU_EXT extern
          #endif

          typedef unsigned char BOOLEAN;
          typedef unsigned char INT8U;
          typedef signed char INT8S;
          typedef unsigned short INT16U;
          typedef signed short INT16S;
          typedef unsigned int INT32U;
          typedef signed int INT32S;
          typedef float FP32;
          typedef double FP64;
          就不解釋了。

          typedef unsigned int OS_STK;
          typedef unsigned int OS_CPU_SR;

          因?yàn)镃M3是32位寬的,所以O(shè)S_STK(堆棧的數(shù)據(jù)類型)被類型重定義為unsigned int。
          因?yàn)镃M3的狀態(tài)寄存器(xPSR)是32位寬的,因此OS_CPU_SR被類型重定義為unsigned int。OS_CPU_SR是在OS_CRITICAL_METHOD方法3中保存cpu狀態(tài)寄存器用的。在CM3中,移植OS_ENTER_CRITICAL(),OS_EXIT_CRITICAL()選方法3是最合適的。

          #define OS_CRITICAL_METHOD 3

          #if OS_CRITICAL_METHOD == 3
          #define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
          #define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
          #endif

          具體定義宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),其中OS_CPU_SR_Save()和OS_CPU_SR_Restore()是用匯編代碼寫的,代碼在os_cpu_a.asm中,到時(shí)再解釋。

          #define OS_STK_GROWTH 1
          CM3中,棧是由高地址向低地址增長的,因此OS_STK_GROWTH定義為1。

          #define OS_TASK_SW() OSCtxSw()
          定義任務(wù)切換宏,OSCtxSw()是用匯編代碼寫的,代碼在os_cpu_a.asm中,到時(shí)再解釋。

          #if OS_CRITICAL_METHOD == 3
          OS_CPU_SR OS_CPU_SR_Save(void);
          void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
          #endif

          void OSCtxSw(void);
          void OSIntCtxSw(void);
          void OSStartHighRdy(void);
          void OS_CPU_PendSVHandler(void);

          void OS_CPU_SysTickHandler(void);
          void OS_CPU_SysTickInit(void);
          INT32U OS_CPU_SysTickClkFreq(void);

          申明幾個(gè)函數(shù),這里要注意最后三個(gè)函數(shù)需要注釋掉,為什么呢?
          OS_CPU_SysTickHandler()定義在os_cpu_c.c中,是SysTick中斷的中斷處理函數(shù),而stm32f10x_it.c,中已經(jīng)有該中斷函數(shù)的定義SysTick_Handler(),這里也就不需要了,是不是很奇怪官方移植版為什么會(huì)這樣弄吧,后面我會(huì)解釋的。
          OS_CPU_SysTickInit()定義在os_cpu_c.c中,用于初始化SysTick定時(shí)器,它依賴于OS_CPU_SysTickClkFreq(),而此函數(shù)我們自己會(huì)實(shí)現(xiàn),所以注釋掉。
          OS_CPU_SysTickClkFreq()定義在BSP.C (MicriumSoftwareEvalBoards)中,而本文移植中并未用到BSP.C,后面我們會(huì)自己實(shí)現(xiàn),因此可以把它注釋掉。

          os_cpu_c.c
          ucosii移植時(shí)需要我們寫10個(gè)相當(dāng)簡單的C函數(shù)。

          OSInitHookBegin()
          OSInitHookEnd()
          OSTaskCreateHook()
          OSTaskDelHook()
          OSTaskIdleHook()
          OSTaskStatHook()
          OSTaskStkInit()
          OSTaskSwHook()
          OSTCBInitHook()
          OSTimeTickHook()

          這些函數(shù)除了OSTaskStkInit(),都是一些hook函數(shù)。這些hook函數(shù)如果不使能的話,都不會(huì)用上,也都比較簡單,看看就應(yīng)該明白了,所以就不介紹。
          下面就說一說OSTaskStkInit()。說之前還是得先說一下任務(wù)切換,因?yàn)槌跏蓟蝿?wù)堆棧,是為任務(wù)切換服務(wù)的。代碼在正常運(yùn)行時(shí),一行一行往下執(zhí)行,怎么才能跑到另一個(gè)任務(wù)(即函數(shù))執(zhí)行呢?首先大家可以回想一下中斷過程,當(dāng)中斷發(fā)生時(shí),原來函數(shù)執(zhí)行的地方(程序計(jì)數(shù)器PC、處理器狀態(tài)寄存器及所有通用寄存器,即當(dāng)前代碼的現(xiàn)場)被保存到棧里面去了,然后開始取中斷向量,跑到中斷函數(shù)里面執(zhí)行。執(zhí)行完了呢,想回到原來函數(shù)執(zhí)行的地方,該怎么辦呢,只要把棧中保存的原來函數(shù)執(zhí)行的信息恢復(fù)即可(把棧中保存的代碼現(xiàn)場重新賦給cpu的各個(gè)寄存器),一切就都回去了,好像什么事都沒發(fā)生一樣。這個(gè)過程大家應(yīng)該都比較熟悉,任務(wù)切換和這有什么關(guān)系,試想一下,如果有3個(gè)函數(shù)foo1(), foo2(),foo3()像是剛被中斷,現(xiàn)場保存到棧里面去了,而中斷返回時(shí)做點(diǎn)手腳(調(diào)度程序的作用),想回哪個(gè)回哪個(gè),是不是就做了函數(shù)(任務(wù))切換了。看到這里應(yīng)該有點(diǎn)明白OSTaskStkInit()的作用了吧,它被任務(wù)創(chuàng)建函數(shù)調(diào)用,所以要在開始時(shí),在棧中作出該任務(wù)好像剛被中斷一樣的假象。(關(guān)于任務(wù)切換的原理邵老師書中的3.06節(jié)有介紹)。
          那么中斷后棧中是個(gè)什么情形呢,<>中9.1.1有介紹,xPSR,PC,LR,R12,R3-R0被自動(dòng)保存到棧中的,R11-R4如果需要保存,只能手工保存。因此OSTaskStkInit()的工作就是在任務(wù)自己的棧中保存cpu的所有寄存器。這些值里R1-R12都沒什么意義,這里用相應(yīng)的數(shù)字代號(hào)(如R1用0x01010101)主要是方便調(diào)試。
          其他幾個(gè):
          xPSR = 0x01000000L,xPSR T位(第24位)置1,否則第一次執(zhí)行任務(wù)時(shí)Fault,
          PC肯定得指向任務(wù)入口,
          R14 = 0xFFFFFFFEL,最低4位為E,是一個(gè)非法值,主要目的是不讓使用R14,即任務(wù)是不能返回的。
          R0用于傳遞任務(wù)函數(shù)的參數(shù),因此等于p_arg。
          OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
          {
          OS_STK *stk;


          (void)opt; /* opt is not used, prevent warning */
          stk = ptos; /* Load stack pointer */

          /* Registers stacked as if auto-saved on exception */
          *(stk) = (INT32U)0x01000000L; /* xPSR */
          *(--stk) = (INT32U)task; /* Entry Point */
          /* R14 (LR) (init value will cause fault if ever used)*/
          *(--stk) = (INT32U)0xFFFFFFFEL;
          *(--stk) = (INT32U)0x12121212L; /* R12 */
          *(--stk) = (INT32U)0x03030303L; /* R3 */
          *(--stk) = (INT32U)0x02020202L; /* R2 */
          *(--stk) = (INT32U)0x01010101L; /* R1 */
          *(--stk) = (INT32U)p_arg; /* R0 : argument */

          /* Remaining registers saved on process stack */
          *(--stk) = (INT32U)0x11111111L; /* R11 */
          *(--stk) = (INT32U)0x10101010L; /* R10 */
          *(--stk) = (INT32U)0x09090909L; /* R9 */
          *(--stk) = (INT32U)0x08080808L; /* R8 */
          *(--stk) = (INT32U)0x07070707L; /* R7 */
          *(--stk) = (INT32U)0x06060606L; /* R6 */
          *(--stk) = (INT32U)0x05050505L; /* R5 */
          *(--stk) = (INT32U)0x04040404L; /* R4 */

          return (stk);
          }

          把OS_CPU_SysTickHandler(), OS_CPU_SysTickInit()注釋掉。

          #define OS_CPU_CM3_NVIC_ST_CTRL (*((volatile INT32U *)0xE000E010))
          #define OS_CPU_CM3_NVIC_ST_RELOAD (*((volatile INT32U *)0xE000E014))
          #define OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018))
          #define OS_CPU_CM3_NVIC_ST_CAL (*((volatile INT32U *)0xE000E01C))

          #define OS_CPU_CM3_NVIC_ST_CTRL_COUNT 0x00010000
          #define OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC 0x00000004
          #define OS_CPU_CM3_NVIC_ST_CTRL_INTEN 0x00000002
          #define OS_CPU_CM3_NVIC_ST_CTRL_ENABLE 0x00000001

          把上面這些宏定義也注釋掉,因?yàn)樗鼈兌加糜贠S_CPU_SysTickHandler(), OS_CPU_SysTickInit()。

          os_cpu_a.asm
          這個(gè)文件包含著必須用匯編寫的代碼。

          EXTERN OSRunning ; External references
          EXTERN OSPrioCur
          EXTERN OSPrioHighRdy
          EXTERN OSTCBCur
          EXTERN OSTCBHighRdy
          EXTERN OSIntNesting
          EXTERN OSIntExit
          EXTERN OSTaskSwHook
          申明這些變量是在其他文件定義的,本文件只做引用(有幾個(gè)好像并未引用,不過沒有關(guān)系)。

          EXPORT OS_CPU_SR_Save ; Functions declared in this file
          EXPORT OS_CPU_SR_Restore
          EXPORT OSStartHighRdy
          EXPORT OSCtxSw
          EXPORT OSIntCtxSw
          EXPORT OS_CPU_PendSVHandler
          申明這些函數(shù)是在本文件中定義的。

          NVIC_INT_CTRL EQU 0xE000ED04 ;中斷控制及狀態(tài)寄存器ICSR的地址
          NVIC_SYSPRI14 EQU 0xE000ED22 ;PendSV優(yōu)先級(jí)寄存器的地址
          NVIC_PENDSV_PRI EQU 0xFF ;PendSV中斷的優(yōu)先級(jí)為255(最低)
          NVIC_PENDSVSET EQU 0x10000000 ;位28為1
          定義幾個(gè)常量,類似C語言中的#define預(yù)處理指令。

          OS_CPU_SR_Save
          MRS R0, PRIMASK ;讀取PRIMASK到R0中,R0為返回值
          CPSID I ;PRIMASK=1,關(guān)中斷(NMI和硬fault可以響應(yīng))
          BX LR ;返回

          OS_CPU_SR_Restore
          MSR PRIMASK, R0 ;讀取R0到PRIMASK中,R0為參數(shù)
          BX LR ;返回

          OSStartHighRdy()由OSStart()調(diào)用,用來啟動(dòng)最高優(yōu)先級(jí)任務(wù),當(dāng)然任務(wù)必須在OSStart()前已被創(chuàng)建。

          OSStartHighRdy
          ;設(shè)置PendSV中斷的優(yōu)先級(jí) #1
          LDR R0, =NVIC_SYSPRI14 ;R0 = NVIC_SYSPRI14
          LDR R1, =NVIC_PENDSV_PRI ;R1 = NVIC_PENDSV_PRI
          STRB R1, [R0] ;*(uint8_t *)NVIC_SYSPRI14 = NVIC_PENDSV_PRI

          ;設(shè)置PSP為0 #2
          MOVS R0, #0 ;R0 = 0
          MSR PSP, R0 ;PSP = R0

          ;設(shè)置OSRunning為TRUE
          LDR R0, =OSRunning ;R0 = OSRunning
          MOVS R1, #1 ;R1 = 1
          STRB R1, [R0] ;OSRunning = 1

          ;觸發(fā)PendSV中斷 #3
          LDR R0, =NVIC_INT_CTRL ;R0 = NVIC_INT_CTRL
          LDR R1, =NVIC_PENDSVSET ;R1 = NVIC_PENDSVSET
          STR R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET

          CPSIE I ;開中斷

          OSStartHang ;死循環(huán),應(yīng)該不會(huì)到這里
          B OSStartHang

          #1.PendSV中斷的優(yōu)先級(jí)應(yīng)該為最低優(yōu)先級(jí),原因在<>的7.6節(jié)已有說明。
          #2.PSP設(shè)置為0,是告訴具體的任務(wù)切換程序(OS_CPU_PendSVHandler()),這是第一次任務(wù)切換。做過切換后PSP就不會(huì)為0了,后面會(huì)看到。
          #3.往中斷控制及狀態(tài)寄存器ICSR(0xE000ED04)第28位寫1即可產(chǎn)生PendSV中斷。這個(gè)<>8.4.5 其它異常的配置寄存器有說明。

          當(dāng)一個(gè)任務(wù)放棄cpu的使用權(quán),就會(huì)調(diào)用OS_TASK_SW()宏,而OS_TASK_SW()就是OSCtxSw()。OSCtxSw()應(yīng)該做任務(wù)切換。但是在CM3中,所有任務(wù)切換都被放到PendSV的中斷處理函數(shù)中去做了,因此OSCtxSw()只需簡單的觸發(fā)PendSV中斷即可。OS_TASK_SW()是由OS_Sched()調(diào)用。

          void OS_Sched (void)
          {
          #if OS_CRITICAL_METHOD == 3
          OS_CPU_SR cpu_sr = 0;
          #endif


          OS_ENTER_CRITICAL();
          if (OSIntNesting == 0) {
          if (OSLockNesting == 0) {
          OS_SchedNew();
          if (OSPrioHighRdy != OSPrioCur) {
          OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
          #if OS_TASK_PROFILE_EN > 0
          OSTCBHighRdy->OSTCBCtxSwCtr++;
          #endif
          OSCtxSwCtr++;
          OS_TASK_SW(); /* 觸發(fā)PendSV中斷 */
          }
          }
          }
          /* 一旦開中斷,PendSV中斷函數(shù)會(huì)執(zhí)行(當(dāng)然要等更高優(yōu)先級(jí)中斷處理完) */
          OS_EXIT_CRITICAL();
          }

          OSCtxSw
          ;觸發(fā)PendSV中斷
          LDR R0, =NVIC_INT_CTRL ;R0 = NVIC_INT_CTRL
          LDR R1, =NVIC_PENDSVSET ;R1 = NVIC_PENDSVSET
          STR R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
          BX LR ;返回

          當(dāng)一個(gè)中斷處理函數(shù)退出時(shí),OSIntExit()會(huì)被調(diào)用來決定是否有優(yōu)先級(jí)更高的任務(wù)需要執(zhí)行。如果有OSIntExit()對(duì)調(diào)用OSIntCtxSw()做任務(wù)切換。

          OSIntCtxSw
          ;觸發(fā)PendSV中斷
          LDR R0, =NVIC_INT_CTRL
          LDR R1, =NVIC_PENDSVSET
          STR R1, [R0]
          BX LR

          看到這里有些同學(xué)可能奇怪怎么OSCtxSw()和OSIntCtxSw()完全一樣,事實(shí)上,這兩個(gè)函數(shù)的意義是不一樣的,OSCtxSw()做的是任務(wù)之間的切換,如任務(wù)A因?yàn)榈却硞€(gè)資源或是做延時(shí)切換到任務(wù)B,而OSIntCtxSw()則是中斷退出時(shí),由中斷狀態(tài)切換到另一個(gè)任務(wù)。由中斷切換到任務(wù)時(shí),CPU寄存器入棧的工作已經(jīng)做完了,所以無需做第二次了(參考邵老師書的3.10節(jié))。這里只不過由于CM3的特殊機(jī)制導(dǎo)致了在這兩個(gè)函數(shù)中只要做觸發(fā)PendSV中斷即可,具體切換由PendSV中斷來處理。

          前面已經(jīng)說過真正的任務(wù)切換是在PendSV中斷處理函數(shù)里做的,由于CM3在中斷時(shí)會(huì)有一半的寄存器自動(dòng)保存到任務(wù)堆棧里,所以在PendSV中斷處理函數(shù)中只需保存R4-R11并調(diào)節(jié)堆棧指針即可。

          PendSV中斷處理函數(shù)偽代碼如下:
          OS_CPU_PendSVHandler()
          {
          if (PSP != NULL) {
          Save R4-R11 onto task stack;
          OSTCBCur->OSTCBStkPtr = SP;
          }
          OSTaskSwHook();
          OSPrioCur = OSPrioHighRdy;
          OSTCBCur = OSTCBHighRdy;
          PSP = OSTCBHighRdy->OSTCBStkPtr;
          Restore R4-R11 from new task stack;
          Return from exception;
          }

          OS_CPU_PendSVHandler ;xPSR, PC, LR, R12, R0-R3已自動(dòng)保存
          CPSID I ;任務(wù)切換期間需要關(guān)中斷

          MRS R0, PSP ;R0 = PSP
          ;如果PSP == 0,跳到OS_CPU_PendSVHandler_nosave執(zhí)行 #1
          CBZ R0, OS_CPU_PendSVHandler_nosave

          ;保存R4-R11到任務(wù)堆棧
          SUBS R0, R0, #0x20 ;R0 -= 0x20
          STM R0, {R4-R11} ;保存R4-R11到任務(wù)堆棧

          ;OSTCBCur->OSTCBStkPtr = SP;
          LDR R1, =OSTCBCur ;R1 = &OSTCBCur
          LDR R1, [R1] ;R1 = *R1 (R1 = OSTCBCur)
          STR R0, [R1] ;*R1 = R0 (*OSTCBCur = SP) #2

          OS_CPU_PendSVHandler_nosave
          ;調(diào)用OSTaskSwHook()
          PUSH {R14} ;保存R14,因?yàn)楹竺嬉{(diào)用函數(shù)
          LDR R0, =OSTaskSwHook ;R0 = &OSTaskSwHook
          BLX R0 ;調(diào)用OSTaskSwHook()
          POP {R14} ;恢復(fù)R14

          ;OSPrioCur = OSPrioHighRdy;
          LDR R0, =OSPrioCur ;R0 = &OSPrioCur
          LDR R1, =OSPrioHighRdy ;R1 = &OSPrioHighRdy
          LDRB R2, [R1] ;R2 = *R1 (R2 = OSPrioHighRdy)
          STRB R2, [R0] ;*R0 = R2 (OSPrioCur = OSPrioHighRdy)

          ;OSTCBCur = OSTCBHighRdy;
          LDR R0, =OSTCBCur ;R0 = &OSTCBCur
          LDR R1, =OSTCBHighRdy ;R1 = &OSTCBHighRdy
          LDR R2, [R1] ;R2 = *R1 (R2 = OSTCBHighRdy)
          STR R2, [R0] ;*R0 = R2 (OSTCBCur = OSTCBHighRdy)

          LDR R0, [R2] ;R0 = *R2 (R0 = OSTCBHighRdy), 此時(shí)R0是新任務(wù)的SP
          ;SP = OSTCBHighRdy->OSTCBStkPtr #3
          LDM R0, {R4-R11} ;從任務(wù)堆棧SP恢復(fù)R4-R11
          ADDS R0, R0, #0x20 ;R0 += 0x20
          MSR PSP, R0 ;PSP = R0,用新任務(wù)的SP加載PSP
          ORR LR, LR, #0x04 ;確保LR位2為1,返回后使用進(jìn)程堆棧 #4
          CPSIE I ;開中斷
          BX LR ;中斷返回

          END

          #1 如果PSP == 0,說明OSStartHighRdy()啟動(dòng)后第一次做任務(wù)切換,而任務(wù)剛創(chuàng)建時(shí)R4-R11已經(jīng)保存在堆棧中了,所以不需要再保存一次了。
          #2 OSTCBStkPtr是任務(wù)控制塊結(jié)構(gòu)體的第一個(gè)變量,所以*OSTCBCur = SP(不是很科學(xué))就是OSTCBCur->OSTCBStkPtr = SP;
          #3 和#2類似。
          #4 因?yàn)樵谥袛嗵幚砗瘮?shù)中使用的是MSP,所以在返回任務(wù)后必須使用PSP,所以LR位2必須為1。

          os_dbg.c
          用于系統(tǒng)調(diào)試,可以不管。

          需要修改的代碼就介紹到這里,如果還有不明白之處,就再看看AN-1018.pdf,邵老師的書和<>。



          關(guān)鍵詞: ucosiistm32移植詳

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();