STM32+RTT 应用(2)

基于usb的IAP + vcom输出log

(1) 基于usb vcom的IAP

  STM32自带IAP(in applacation programming) 功能,通过BOOT引脚选择启动模式,从系统存储器启动即可进入。ST原生的IAP需要通过uart下载,使用起来有一定局限性,下载速度、可靠性都不理想。

图1 启动模式选择

BOOT

  本文讨论基于USB Vcom(virtual comport)的IAP 实现,手里的STM32F103C8T6支持USB 2.0 full-speed,同时USB口也兼具供电作用。使用USB接口进行IAP有几个好处:
<1> 使用方便,目标板仅需连一根USB线,普通用户即可轻松操作.
<2> 高速下载,USB full-speed理论传输速率是12Mbps;同时Vcom是可靠传输,每包数据均由CRC校验,出错随即重传.
<3> 附带log输出作用,当然,目标板和PC端的交互是可以任意的.

  USB spec比较复杂,从0开始自己实现显然不明智,幸好ST官方有USB库,本文使用的是STM32_USB-FS-Device_Lib_V4.0.0 ,对于其中的project VirtualComport_Loopback进行扩展。需要实现与PC端的handshake,接收Host发送的数据并写入内部flash,过程中的log传回到PC。
<1> 数据发送函数

uint32_t CDC_Send_DATA (uint8_t *ptrBuffer, uint8_t Send_length);

长度不能超过VIRTUAL_COM_PORT_DATA_SIZE (64) ,发送完成packet_sent 标志置1.
<2> 数据接收函数

uint32_t CDC_Receive_DATA(void)

这个函数仅是设置ENDP3 valid,可以接收数据,真正数据接收完成放在Receive_Buffer,长度为Receive_length,并将标志packet_receive置1.

  有了发送和接收函数就可以和PC端交互了,接收bin文件及传回log都是都过这两个函数实现。
  官方的IAP需要通过BOOT引脚才能进入,本文通过与PC handshake自动识别是进入IAP还是正常的APP。Handshake过程比较简单:PC端检测VCOM口,当有一个VCOM生成就发送标志’ 0xa5’,目标板收到标志即确认进入IAP模式,并返回ACK ‘0x5a’,PC检测到’0x5a’就开始发送bin文件。与通过引脚方式相比这中方式需要多花一些时间进行handshake,但是好处也是显而易见的:该方式无需人工干预,可以自动下载。

(2) 写入Flash

  IAP程序收到bin数据后写入flash,STM32内部的flash写入前需要unlock,一次写入2bytes(half word)。写flash的步骤是

FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
......
FLASH_ProgramHalfWord(......);
......
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
FLASH_Lock();

PS.读flash数据比较简单,直接通过地址读取”(__IO u32) (Address)”

(3) 跳转到user APP

  跳转的程序比较简单,网上都可以找到,官方的也有。需要注意的是:跳转前要设置control 寄存器到默认,关闭中断以防止意外情况,最好将用到的设备deinit。跳转的code 如下:

void jump_to_user()
{
    u32  JumpAddress;
    pFunction Jump_To_Application;
    if (((*(__IO u32 *)FW_START) & 0x2FFE0000 ) == 0x20000000)  
    {  
        print_s("Execute user Program\r\n", 22); 
        JumpAddress = *(__IO u32*) (FW_START + 4);  
        Jump_To_Application = (pFunction) JumpAddress;  
        __set_PSP(*(__IO u32*) FW_START);  
        __set_CONTROL(0);
        __set_MSP(*(__IO u32*) FW_START);  
        __ASM volatile("CPSID I");
       Jump_To_Application();  
        //Never come back
    }  
    else  
    {  
        print_s("no user Program\r\n", 17);  
    }  
}

(4) 传回log到PC

  直接将需要输出的log通过VCOM发送就可以传到PC端,这里使用的是sync方式,数据真实传输完毕才会返回,防止log太多冲掉前面的。

扫描二维码关注公众号,回复: 1744871 查看本文章
void print_s(u8 *ptrBuffer, u8 Send_length)
{
    if (bDeviceState == CONFIGURED){
        CDC_Send_DATA (ptrBuffer, Send_length);
        while(packet_sent != 1); 
    }
}

其中,packet_sent 标志要添加volatile声明,不然会被编译器优化为dead loop.

(5) user APP的注意点

  IAP程序下载的user APP放的地址不一定是flash首地址,所以user project的memory map 和 vector table要一起修改。

图2 Memory map修改

memory

  在project option对话框的Target标签,将起始地址 &大小修改为需要的配置,这里预留给IAP程序16K,地址从0x8004000 开始,大小48K。从生成的map文件可以看到函数地址都相应改变了:

Reset_Handler    0x080068f1   Thumb Code
ADC1_2_IRQHandler    0x08006907   Thumb Code
......

另外,由于IAP下载的是bin文件,要执行自定义命令从axf生产bin。

图3 生成bin

bin

在main函数入口向量表地址也要对应设置,否则中断无法执行.

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x4000);

(6) RTT vcom输出log

  RTT原来使用的是uart输出log,同样,这里可以使用USB vcom输出log,将ST官方usb lib库加到RTT中即可,修改rt_kprintf()函数通过vcom输出log.

void rt_print(const char *str)
{
    u8 *ptr = (u8 *)str;
    char send_length = rt_strlen(str);
    if(bDeviceState == CONFIGURED && 1 == tool_exist)
    {
        CDC_Send_DATA (ptr , send_length>=VIRTUAL_COM_PORT_DATA_SIZE-1 ? VIRTUAL_COM_PORT_DATA_SIZE-1: send_length);
    } 
}

  总结,使用基于USB的IAP可以很方便的实现软件升级,通过handshake机制使得自动下载得以实现,并且可以同时输出log信息。该方案的不足是IAP程序本身占用宝贵的flash空间,handshake需要耗费一些时间。

未解决的问题:
<1> RTT自带有USB协议栈实现,花了两天时间,解决各种build error,最终还是无法识别usb,而用ST官方lib就没问题。
<2> 跳转后的user APP无法使用usb功能,无法识别usb,而直接下载执行则没问题。
  这两个问题从打印log可以看到是一样的现象,USB istr一直报SUSPend & Expected Start Of Frame中断,猜测可能是因为:开发板上的DP直接连到了3.3v,这样一插上PC都会识别到,但是usb中断还没来得及执行。这就导致目标板没有响应PC,最终无法识别。 而lib库的code可以看到DP上拉电阻是在PowerOn()函数才会加上,PowerOff()会去掉。有时间试试把开发板上焊死的上拉电阻断开,通过GPIO来控制。

猜你喜欢

转载自blog.csdn.net/songdawww/article/details/51301640