GD32F407,基于ymode协议 RS485IAP升级

由于项目需要,了解IAP对程序进行升级操作,可以不用st—link烧写,减轻工作量。

IAP是先运行bootloader,再去运行APP程序。

bootloader程序

  bootloader最重要是2个部分,从应用层读取升级文件,覆盖之前的APP程序(当然也可以不用覆盖以前的APP程序,直接运行接受到的升级程序,那么需要注意新APP的flash起始地址。),还有从BootLoader跳转到APP程序,代码如下:



#define APPLICATION_ADDRESS 0x08004000
//检查栈顶地址是否合法.
if((*( __IO uint32_t*)(APPLICATION_ADDRESS)&0x2FFE0000)==0x20000000)    {

    
            JumpAddress = *( __IO uint32_t*) (APPLICATION_ADDRESS + 4) ;  
            // 指向用户程序复位函数所在的地址
            Jump_To_Application = ( pFunction ) JumpAddress;
            //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
                    __set_MSP( *( __IO uint32_t* )  APPLICATION_ADDRESS );                    
            /* Jump to application */
            Jump_To_Application();
    } else{
        printf("error\r\n");
    }

FLASH的读写操作,要把APP的.bin文件写入FLASH中。

在写入之前要先擦除FLASH。


/*==============================================================================
    函数名      : IapEraseSector
    功能        : 擦除相关扇区
    输入参数说明: addr_start: 起始地址
                 
------------------------------------------------------------------------------*/

fmc_state_enum IapEraseSector(uint32_t addr_start)
{
	uint8_t i;
	printf("\r\nerase on\r\n");
	fmc_unlock();
	fmc_flag_clear(FMC_FLAG_END);
	fmc_flag_clear(FMC_FLAG_WPERR);
	fmc_flag_clear(FMC_FLAG_PGSERR);
	fmc_flag_clear(FMC_FLAG_PGMERR);
	printf("%#X\r\n",addr_start);
	
	if(FMC_READY != fmc_sector_erase(CTL_SN(1)))
	{
			
		return -1;
	}
	
	fmc_flag_clear(FMC_FLAG_END);
	fmc_flag_clear(FMC_FLAG_WPERR);
	fmc_flag_clear(FMC_FLAG_PGSERR);
	fmc_flag_clear(FMC_FLAG_PGMERR);
	
	fmc_lock();	
	printf("erase off\r\n");
  return 0;

}

FLASH 写操作

/*==============================================================================
    函数名      : FLASH_If_Write
    功能        : 写入FLASH
    输入参数说明: 
                  dwAddr: 写入Flash的起始地址, 从0开始的偏移地址
                  dwLen : 写入数据的字节数
                  pbyBuf: 写入数据的buf
    返回值说明  : 成功返回0; 失败返回-1或错误码
------------------------------------------------------------------------------*/


uint32_t FLASH_If_Write(uint32_t dwAddr, uint8_t *pbyBuf, uint32_t dwLen)
{
		
	uint32_t i=0;
	uint32_t new_addr;
	uint8_t data=0;
    	fmc_unlock();
	for(i=0;i<dwLen;i++)
	{
		data=*(pbyBuf+i);
		new_addr = dwAddr+i*sizeof(uint8_t);
		if(FMC_READY != fmc_byte_program(new_addr,data))
		{
			return -1;
		}
		fmc_flag_clear(FMC_FLAG_END);
		fmc_flag_clear(FMC_FLAG_WPERR);
		fmc_flag_clear(FMC_FLAG_PGSERR);
		fmc_flag_clear(FMC_FLAG_PGMERR);
	}
	fmc_lock();	
	return 0;


}

YMODE协议的移植

**
  * @brief  使用ymodem协议接收文件
  * @param  buf: 第一个字节的地址
  * @retval 文件的大小
  */
int32_t Ymodem_Receive(uint8_t *buf)
{
  uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
  int32_t i, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;
  uint32_t flashdestination,ramsource;

  /* Initialize flashdestination variable */
  flashdestination = APPLICATION_ADDRESS;
	//SerialPutString("*****test1****\n\r");
	//printf("buf:%s\r\n",buf);
  for (session_done = 0, errors = 0, session_begin = 0;;)
    {
        for (packets_received = 0, file_done = 0, buf_ptr = buf;;)
        {
            switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT))
            {
            case 0:
                errors = 0;
                switch (packet_length)
                {
                    //发送端终止
                case -1:
                    Send_Byte(ACK);
                    return 0;
                    //结束传输
                case 0:
                    Send_Byte(ACK);
                    file_done = 1;
                    break;
                    //正常的数据包
                default:
                    if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
                    {
                        Send_Byte(NAK);
                    }
                    else
                    {
                        if (packets_received == 0)
                        {
                            //文件名数据包
                            if (packet_data[PACKET_HEADER] != 0)
                            {
                                //文件名数据包有效数据区域
                                for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
                                {
                                    FileName[i++] = *file_ptr++;
                                }
                                FileName[i++] = '\0';
                                for (i = 0, file_ptr++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
                                {
                                    file_size[i++] = *file_ptr++;
                                }
                                file_size[i++] = '\0';
                                Str2Int(file_size, &size);
 
                                //测试数据包是否过大
                                if (size > (FMC_PAGE_SIZE - 1))
                                {
                                    //结束
                                    Send_Byte(CA);
                                    Send_Byte(CA);
                                    return -1;
                                }
                                
                                IapEraseSector(flashdestination);  //清空flash扇区
                                Send_Byte(ACK);
                                Send_Byte(CRC16);
                            }
                            //文件名数据包空,结束传输
                            else
                            {
                                Send_Byte(ACK);
                                file_done = 1;
                                session_done = 1;
                                break;
                            }
                        }
                        //数据包
                        else
                        {
                            memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
                            ramsource = (uint32_t)buf;
                            if(packet_length == PACKET_SIZE)  //整包
                            {
                                FLASH_If_Write(flashdestination, (uint8_t *)ramsource, packet_length);  //写flash
                                if (*(uint32_t *)flashdestination != *(uint32_t *)ramsource)  //错误
                                {
                                    Send_Byte(CA);
                                    Send_Byte(CA);
                                    return -2;
                                }
                                flashdestination += packet_length;
                            }
                            else  //尾包
                            {
                                FLASH_If_Write(flashdestination, (uint8_t *)ramsource, (size%PACKET_SIZE));  //写flash
                            }
                           
                            Send_Byte(ACK);
                        }
                        packets_received++;
                        session_begin = 1;
                    }
                }
                break;
            case 1:
                Send_Byte(CA);
                Send_Byte(CA);
                return -3;
            default:
                if (session_begin > 0)
                {
                    errors++;
                }
                if (errors > MAX_ERRORS)
                {
                    Send_Byte(CA);
                    Send_Byte(CA);
                    return 0;
                }
                Send_Byte(CRC16);
                break;
            }
            if (file_done != 0)
            {
                break;
            }
        }
        if (session_done != 0)
        {
            break;
        }
    }
    return (int32_t)size;

}

APP程序没什么大的改动,需要在keil修改ROM的起始地址。

这里需要了解一下GD32F4的闪存结构。F4系列FLSASH是扇区的概念,booyloader放在扇区0

APP程序放在了扇区1里。

我的APP程序起始地址为0x08004000 大小是14K左右

 




注意:

        SCB->VTOR = 0x08004000;
        要在APP程序的开头加一句程序,中断向量表跳偏移地址 ,地址为你的APP起始地址。
        如果不加这句,程序就会跳不进来。
最后在生成文件的时候要生成.bin文件。

代码自取:

链接:https://pan.baidu.com/s/1JUxivJ-m1lIxnEiK2ES1hA 
提取码:r4q1

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
GD32F407的IAP程序是指在应用程序中实现程序更新的功能。以下是一个简单的GD32F407的IAP程序示例,仅供参考: ```c #include "gd32f4xx.h" #define APP_ADDRESS 0x08008000 // 应用程序存储地址 #define PAGE_SIZE 0x400 // 一页大小 #define APP_SIZE 0x8000 // 应用程序大小 #define FLASH_END_ADDR 0x080FFFFF // Flash结束地址 const uint32_t* app_addr = (const uint32_t*)APP_ADDRESS; uint32_t app_size = 0; uint32_t app_crc = 0; void iap_erase_flash(uint32_t address, uint32_t size) { uint32_t end_addr; FLASH_Unlock(); end_addr = address + size - 1; while (address < end_addr) { FLASH_ErasePage(address); address += PAGE_SIZE; } FLASH_Lock(); } void iap_write_flash(const uint32_t* data, uint32_t address, uint32_t size) { uint32_t end_addr; FLASH_Unlock(); end_addr = address + size - 1; while (address < end_addr) { FLASH_ProgramWord(address, *data++); address += 4; } FLASH_Lock(); } uint32_t iap_verify_flash(const uint32_t* data, uint32_t address, uint32_t size) { uint32_t i, crc = 0; for (i = 0; i < size / 4; i++) { crc = crc32_update(crc, data[i]); } return crc; } uint32_t iap_get_app_size(void) { uint32_t i, size = 0; for (i = 0; i < APP_SIZE / 4; i++) { if (app_addr[i] != 0xFFFFFFFF) { size = i * 4; } } return size; } uint32_t iap_get_app_crc(void) { return iap_verify_flash(app_addr, APP_ADDRESS, app_size); } void iap_jump_to_app(void) { void (*app_entry)(void); app_entry = (void (*)(void))APP_ADDRESS; __set_MSP(*((uint32_t*)APP_ADDRESS)); app_entry(); } int main(void) { app_size = iap_get_app_size(); app_crc = iap_get_app_crc(); // 接收应用程序数据并存储到Flash中 // ... // 擦除应用程序存储区域 iap_erase_flash(APP_ADDRESS, APP_SIZE); // 写入应用程序数据到Flash中 iap_write_flash(app_addr, APP_ADDRESS, app_size); // 验证应用程序数据 if (app_crc == iap_verify_flash(app_addr, APP_ADDRESS, app_size)) { // 跳转到应用程序入口 iap_jump_to_app(); } while (1) { // 程序更新失败,做相应处理 // ... } } ``` 需要注意的是,这只是一个简单的示例程序,并不能完全满足实际应用的需求。在实际应用中,需要根据具体情况进行优化和修改。同时,为了确保程序更新的可靠性,还需要进行更多的测试和验证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值