完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
摘要
本节主要记录自己学习Ardupilot stm32f1 Bootloader,欢迎批评指正! 1.Bootloader代码执行 int main(void) { unsigned timeout = 0; /* 做板层初始化------do board-specific initialisation */ board_init(); //主要配置LED和串口2 #if defined(INTERFACE_USART) || defined (INTERFACE_USB) /*探测连接串口,以决定是否在引导加载程序中等待?--- XXX sniff for a USART connection to decide whether to wait in the bootloader? */ timeout = BOOTLOADER_DELAY; #endif #ifdef INTERFACE_I2C # error I2C bootloader detection logic not implemented #endif /*如果应用程序留下一个cookie说我们应该等待,然后等待---- if the app left a cookie saying we should wait, then wait */ //Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。 if (should_wait()) { timeout = BOOTLOADER_DELAY; } #ifdef BOARD_FORCE_BL_PIN /* if the force-BL pin state matches the state of the pin, wait in the bootloader forever */ //如果强制BL引脚状态与PIN的状态匹配,则在Bootloader中永远等待 if (BOARD_FORCE_BL_VALUE == gpio_get(BOARD_FORCE_BL_PORT, BOARD_FORCE_BL_PIN)) { timeout = 0xffffffff; } #endif /*在备份寄存器零点中查找Bootloader值中的魔法等待--- look for the magic wait-in-bootloader value in backup register zero */ /*如果我们不希望在引导加载程序中等待,尝试立即启动。---- if we aren't expected to wait in the bootloader, try to boot immediately */ if (timeout == 0) { /*尝试立即启动--------try to boot immediately */ jump_to_app(); //如果有应用程序,代码将会跳进应用程序,不返回。 /*如果我们返回,没有应用程序;去引导加载程序并停留在那里。--- if we returned, there is no app; go to the bootloader and stay there */ timeout = 0; } /*配置激活bootloader时钟---- configure the clock for bootloader activity */ clock_init(); /*启动接口,采用串口---- start the interface */ cinit(BOARD_INTERFACE_CONFIG, USART); while (1) { /* run the bootloader, possibly coming back after the timeout */ //运行引导加载程序,可能在超时后返回 bootloader(timeout); /* look to see if we can boot the app */ //看看我们能不能启动应用程序 jump_to_app(); /* boot failed; stay in the bootloader forever next time */ //引导失败;下次永远留在引导加载程序中 timeout = 0; } } 1.board_init(); static void board_init(void) { /*初始化led----initialise LEDs */ rcc_peripheral_enable_clock(&BOARD_CLOCK_LEDS_REGISTER, BOARD_CLOCK_LEDS); //使能PB端口时钟 gpio_set_mode(BOARD_PORT_LEDS, //设置PB GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, BOARD_PIN_LED_BOOTLOADER | BOARD_PIN_LED_ACTIVITY); //PB15,PB14 BOARD_LED_ON(BOARD_PORT_LEDS,BOARD_PIN_LED_BOOTLOADER | BOARD_PIN_LED_ACTIVITY); //点亮led /*如果有一个,强制引导加载程序引脚---if we have one, enable the force-bootloader pin */ #ifdef BOARD_FORCE_BL_PIN //PB5 rcc_peripheral_enable_clock(&BOARD_FORCE_BL_CLOCK_REGISTER, BOARD_FORCE_BL_CLOCK_BIT); gpio_set(BOARD_FORCE_BL_PORT, BOARD_FORCE_BL_PIN); gpio_set_mode(BOARD_FORCE_BL_PORT, GPIO_MODE_INPUT, BOARD_FORCE_BL_PULL, BOARD_FORCE_BL_PIN); #endif /*启用备份寄存器---- enable the backup registers */ rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); #ifdef INTERFACE_USART /*配置串口引脚-------configure usart pins */ rcc_peripheral_enable_clock(&BOARD_USART_PIN_CLOCK_REGISTER, BOARD_USART_PIN_CLOCK_BIT); gpio_set_mode(BOARD_PORT_USART, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, BOARD_PIN_TX); //对应----UART2 /*配置串口时钟使能----configure USART clock */ rcc_peripheral_enable_clock(&BOARD_USART_CLOCK_REGISTER, BOARD_USART_CLOCK_BIT); #endif #ifdef INTERFACE_I2C # error I2C GPIO config not handled yet #endif } 总结:板层初始化主要完成LED初始化,串口初始化 2.jump_to_app(); void jump_to_app() { const uint32_t *app_base = (const uint32_t *)APP_LOAD_ADDRESS; //app的入口地址是0x08001000,也就是说有4k字节的地址给bootloader使用 /* * We refuse to program the first word of the app until the upload is marked * complete by the host. So if it's not 0xffffffff, we should try booting it. */ //我们拒绝程序的第一个字的应用程序,直到上传标记完成的主机。因此,如果不是0xFFFFFFF,我们应该尝试启动它。 if (app_base[0] == 0xffffffff) { return; } /* * The second word of the app is the entrypoint; it must point within the * flash area (or we have a bad flash). */ //应用程序的第二个字是入口点;它必须指向闪存区域内(或者我们的闪存不好) if (app_base[1] < APP_LOAD_ADDRESS) { return; } if (app_base[1] >= (APP_LOAD_ADDRESS + board_info.fw_size)) //大于板子的APP flash大小=总FLASH去掉预留和bootloader { return; } /*flash短暂锁定闪存程序和擦除控制器用于防止对闪存的虚假写入---just for paranoia's sake */ flash_lock(); /*关闭系统中断和计数-----kill the systick interrupt */ systick_interrupt_disable(); systick_counter_disable(); /* 重新初始化串口和USB----deinitialise the interface */ cfini(); /*重置时钟---- reset the clock */ clock_deinit(); /*重置板层初始化----deinitialise the board */ board_deinit(); /*将异常处理程序切换到应用程序---- switch exception handlers to the application */ SCB_VTOR = APP_LOAD_ADDRESS; //切换到应用程序地址 /* extract the stack and entrypoint from the app vector table and go */ //从app的中断矢量表中,提取堆栈入口点 do_jump(app_base[0], app_base[1]); } 3.do_jump(); static void do_jump(uint32_t stacktop, uint32_t entrypoint) { asm volatile( "msr msp, %0 n" //msr通用寄存器值传入特殊功能寄存器 "bx %1 n" : : "r"(stacktop), "r"(entrypoint) :); // // just to keep noreturn happy for (;;) ; } 1 2 3 4 5 6 7 8 9 10 上面的代码等价于下面的两行汇编指令 msr msp stacktop bx entrypoint 备注:asm violate (“movl %1,%0” : “=r” (result) : “m” (input)); “movl %1,%0"是指令模板;”%0"和"%1"代表指令的操作数,称为占位符,内嵌汇编靠它们将C 语言表达式与指令操作数相对应。指令模板后面用小括号括起来的是C语言表达式,本例中只有两个:“result"和"input”,他们按照出现的顺序分 别与指令操作数"%0","%1"对应;注意对应顺序:第一个C 表达式对应"%0";第二个表达式对应"%1",依次类推,操作数至多有10 个,分别用"%0","%1"…"%9"表示。在每个操作数前面有一个用引号括起来的字符串,字符串的内容是对该操作数的限制或者说要求。 “result"前面的限制字符串是”=r",其中"=“表示"result"是输出操作数,“r” 表示需要将"result"与某个通用寄存器相关联,先将操作数的值读入寄存器,然后在指令中使用相应寄存器,而不是"result"本身,当然指令执行 完后需要将寄存器中的值存入变量"result”,从表面上看好像是指令直接对"result"进行操作,实际上GCC做了隐式处理,这样我们可以少写一 些指令。“input"前面的"r"表示该表达式需要先放入某个寄存器,然后在指令中使用该寄存器参加运算。 C表达式或者变量与寄存器的关系由GCC自动处理,我们只需使用限制字符串指导GCC如何处理即可。限制字符必须与指令对操作数的要求相匹配,否则产生的 汇编代码将会有错,读者可以将上例中的两个"r”,都改为"m"(m表示操作数放在内存,而不是寄存器中),编译后得到的结果是: movl input, result 很明显这是一条非法指令,因此限制字符串必须与指令对操作数的要求匹配。例如指令movl允许寄存器到寄存器,立即数到寄存器等,但是不允许内存到内存的操作,因此两个操作数不能同时使用"m"作为限定字符。 所以这里我们只需要设定飞控程序的入口地址是pc的初值就可以满足,Bootloader跳转到飞控应用程序。 注意:jump_to_app()函数为什么使用两次:可以这样理解:假如我们的飞控已经下载BOOTLOADER,现在正在烧写应用程序,就直接跳转到应用程序,但是如果我们只有Bootloader没有烧写应用程序,那代码一定要实时检查是否有应用程序到来。 4.bootloader(timeout) void bootloader(unsigned timeout) { //这个类型的引导程序,无论从u***或串口,将决定什么类型的端口第一时间接收bootloader命令是有效的 bl_type = NONE; // The type of the bootloader, whether loading from USB or USART, will be determined by on what port the bootloader recevies its first valid command. //上载前强制擦除 uint32_t address = board_info.fw_size; /* force erase before upload will work */ uint32_t first_word = 0xffffffff; /* 重新)启动计时器系统(re)start the timer system */ systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); systick_set_reload(board_info.systick_mhz * 1000); /* 1ms tick, magic number */ systick_interrupt_enable(); systick_counter_enable(); /* 如果我们正在处理超时,请启动它运行if we are working with a timeout, start it running */ if (timeout) { timer[TIMER_BL_WAIT] = timeout; } /* make the LED blink while we are idle */ //使LED在我们空闲时闪烁 led_set(LED_BLINK); while (true) { volatile int c; int arg; static union { uint8_t c[256]; uint32_t w[64]; } flash_buffer; //等待命令字节---- Wait for a command byte led_off(LED_ACTIVITY); do { /* if we have a timeout and the timer has expired, return now */ if (timeout && !timer[TIMER_BL_WAIT]) { return; } /* try to get a byte from the host */ c = cin_wait(0); } while (c < 0); led_on(LED_ACTIVITY); // handle the command byte switch (c) { // sync // // command: GET_SYNC/EOC // reply: INSYNC/OK // case PROTO_GET_SYNC: /* expect EOC */ if (!wait_for_eoc(2)) { goto cmd_bad; } break; // get device info // // command: GET_DEVICE/ // BL_REV reply: // BOARD_ID reply: // BOARD_REV reply: // FW_SIZE reply: // VEC_AREA reply // bad arg reply: INSYNC/INVALID // case PROTO_GET_DEVICE: /* expect arg then EOC */ arg = cin_wait(1000); if (arg < 0) { goto cmd_bad; } if (!wait_for_eoc(2)) { goto cmd_bad; } switch (arg) { case PROTO_DEVICE_BL_REV: cout((uint8_t *)&bl_proto_rev, sizeof(bl_proto_rev)); break; case PROTO_DEVICE_BOARD_ID: cout((uint8_t *)&board_info.board_type, sizeof(board_info.board_type)); break; case PROTO_DEVICE_BOARD_REV: cout((uint8_t *)&board_info.board_rev, sizeof(board_info.board_rev)); break; case PROTO_DEVICE_FW_SIZE: cout((uint8_t *)&board_info.fw_size, sizeof(board_info.fw_size)); break; case PROTO_DEVICE_VEC_AREA: for (unsigned p = 7; p <= 10; p++) { uint32_t bytes = flash_func_read_word(p * 4); cout((uint8_t *)&bytes, sizeof(bytes)); } break; default: goto cmd_bad; } break; // erase and prepare for programming // // command: ERASE/EOC // success reply: INSYNC/OK // erase failure: INSYNC/FAILURE // case PROTO_CHIP_ERASE: /* expect EOC */ if (!wait_for_eoc(2)) { goto cmd_bad; } #if defined(TARGET_HW_PX4_FMU_V4) if (check_silicon()) { goto bad_silicon; } #endif // clear the bootloader LED while erasing - it stops blinking at random // and that's confusing led_set(LED_ON); // erase all sectors flash_unlock(); for (int i = 0; flash_func_sector_size(i) != 0; i++) { flash_func_erase_sector(i); } // enable the LED while verifying the erase led_set(LED_OFF); // verify the erase for (address = 0; address < board_info.fw_size; address += 4) if (flash_func_read_word(address) != 0xffffffff) { goto cmd_fail; } address = 0; // resume blinking led_set(LED_BLINK); break; // program bytes at current address // // command: PROG_MULTI/ // success reply: INSYNC/OK // invalid reply: INSYNC/INVALID // readback failure: INSYNC/FAILURE // case PROTO_PROG_MULTI: // program bytes // expect count arg = cin_wait(50); if (arg < 0) { goto cmd_bad; } // sanity-check arguments if (arg % 4) { goto cmd_bad; } if ((address + arg) > board_info.fw_size) { goto cmd_bad; } if (arg > sizeof(flash_buffer.c)) { goto cmd_bad; } for (int i = 0; i < arg; i++) { c = cin_wait(1000); if (c < 0) { goto cmd_bad; } flash_buffer.c = c; } if (!wait_for_eoc(200)) { goto cmd_bad; } if (address == 0) { #if defined(TARGET_HW_PX4_FMU_V4) if (check_silicon()) { goto bad_silicon; } #endif // save the first word and don't program it until everything else is done first_word = flash_buffer.w[0]; // replace first word with bits we can overwrite later flash_buffer.w[0] = 0xffffffff; } arg /= 4; for (int i = 0; i < arg; i++) { // program the word flash_func_write_word(address, flash_buffer.w); // do immediate read-back verify if (flash_func_read_word(address) != flash_buffer.w) { goto cmd_fail; } address += 4; } break; // fetch CRC of the entire flash area // // command: GET_CRC/EOC // reply: // case PROTO_GET_CRC: // expect EOC if (!wait_for_eoc(2)) { goto cmd_bad; } // compute CRC of the programmed area uint32_t sum = 0; for (unsigned p = 0; p < board_info.fw_size; p += 4) { uint32_t bytes; if ((p == 0) && (first_word != 0xffffffff)) { bytes = first_word; } else { bytes = flash_func_read_word(p); } sum = crc32((uint8_t *)&bytes, sizeof(bytes), sum); } cout_word(sum); break; // read a word from the OTP // // command: GET_OTP/ // reply: case PROTO_GET_OTP: // expect argument { uint32_t index = 0; if (cin_word(&index, 100)) { goto cmd_bad; } // expect EOC if (!wait_for_eoc(2)) { goto cmd_bad; } cout_word(flash_func_read_otp(index)); } break; // read the SN from the UDID // // command: GET_SN/ // reply: case PROTO_GET_SN: // expect argument { uint32_t index = 0; if (cin_word(&index, 100)) { goto cmd_bad; } // expect EOC if (!wait_for_eoc(2)) { goto cmd_bad; } cout_word(flash_func_read_sn(index)); } break; // read the chip ID code // // command: GET_CHIP/EOC // reply: case PROTO_GET_CHIP: { // expect EOC if (!wait_for_eoc(2)) { goto cmd_bad; } cout_word(get_mcu_id()); } break; // read the chip description // // command: GET_CHIP_DES/EOC // reply: case PROTO_GET_CHIP_DES: { uint8_t buffer[MAX_DES_LENGTH]; unsigned len = MAX_DES_LENGTH; // expect EOC if (!wait_for_eoc(2)) { goto cmd_bad; } len = get_mcu_desc(len, buffer); cout_word(len); cout(buffer, len); } break; #ifdef BOOT_DELAY_ADDRESS case PROTO_SET_DELAY: { /* Allow for the bootloader to setup a boot delay signature which tells the board to delay for at least a specified number of seconds on boot. */ int v = cin_wait(100); if (v < 0) { goto cmd_bad; } uint8_t boot_delay = v & 0xFF; if (boot_delay > BOOT_DELAY_MAX) { goto cmd_bad; } // expect EOC if (!wait_for_eoc(2)) { goto cmd_bad; } uint32_t sig1 = flash_func_read_word(BOOT_DELAY_ADDRESS); uint32_t sig2 = flash_func_read_word(BOOT_DELAY_ADDRESS + 4); if (sig1 != BOOT_DELAY_SIGNATURE1 || sig2 != BOOT_DELAY_SIGNATURE2) { goto cmd_bad; } uint32_t value = (BOOT_DELAY_SIGNATURE1 & 0xFFFFFF00) | boot_delay; flash_func_write_word(BOOT_DELAY_ADDRESS, value); if (flash_func_read_word(BOOT_DELAY_ADDRESS) != value) { goto cmd_fail; } } break; #endif // finalise programming and boot the system // // command: BOOT/EOC // reply: INSYNC/OK // case PROTO_BOOT: // expect EOC if (!wait_for_eoc(1000)) { goto cmd_bad; } // program the deferred first word if (first_word != 0xffffffff) { flash_func_write_word(0, first_word); if (flash_func_read_word(0) != first_word) { goto cmd_fail; } // revert in case the flash was bad... first_word = 0xffffffff; } // send a sync and wait for it to be collected sync_response(); delay(100); // quiesce and jump to the app return; case PROTO_DEBUG: // XXX reserved for ad-hoc debugging as required break; default: continue; } // we got a command worth syncing, so kill the timeout because // we are probably talking to the uploader timeout = 0; // Set the bootloader port based on the port from which we received the first valid command if (bl_type == NONE) { bl_type = last_input; } // send the sync response for this command sync_response(); continue; cmd_bad: // send an 'invalid' response but don't kill the timeout - could be garbage invalid_response(); continue; cmd_fail: // send a 'command failed' response but don't kill the timeout - could be garbage failure_response(); continue; #if defined(TARGET_HW_PX4_FMU_V4) bad_silicon: // send the bad silicon response but don't kill the timeout - could be garbage bad_silicon_response(); continue; #endif } } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1786 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1622 浏览 1 评论
1089 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
730 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1680 浏览 2 评论
1941浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
739浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
573浏览 3评论
598浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
560浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 09:59 , Processed in 0.882919 second(s), Total 76, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号