s9keaz128 UART Bootloader 上位机篇(下)

一:前言

网络上充斥着各种各样的串口升级方案,基本都是基于Y-Mode协议下载。采用这种升级方案学习还是可以,用在产品上还是有点欠缺。如下载完成后,需要把时间发送到设备就不好搞了。如下这个设备返回的信息,最后一项就是固件更新时间。

固件名称: KEA128

编译时间: 2022/03/02 14:25:18

固件版本: 1.0.3

硬件版本: 1.0.3

产品序列号: 30304536001 A2210000002D

功能名称: App

固件更新时间: 2022/03/02 14:26:46

串口升级演示视频

项目源码下载:

【免费】s9keaz128串口升级方案1:上位机qt5源码2:单片机底层与应用程序3:烧写文档4:原理图资源-CSDN文库

二:升级协议

1:获取固件信息

PC发送:

头部

功能

数据长度

数据

校验

结束

55 AA

01

00 00

-

校验和

5A

设备返回:

头部

功能

数据长度

数据

校验

结束

55 AA

01

LL   HH

 “版本”

校验和

5A

2:运行命令

PC发送:

头部

功能

数据长度

数据

校验

结束

55 AA

02

04 00

地址(xx xx xx xx)

校验和

5A

设备返回:

头部

功能

数据长度

数据

校验

结束

55 AA

02

01   00

 00-失败  01-成功

校验和

5A

3:擦除Flash命令

PC发送:

头部

功能

数据长度

数据

校验

结束

55 AA

03

08 00

地址(AA AA AA AA)

长度(LL LL LL LL)

校验和

5A

设备返回:

头部

功能

数据长度

数据

校验

结束

55 AA

03

01   00

 00-失败  01-成功

校验和

5A

4:写数据命令

PC发送:

头部

功能

数据长度

数据

校验

结束

55 AA

04

LL LL

数据

校验和

5A

设备返回:

头部

功能

数据长度

数据

校验

结束

55 AA

04

01   00

 00-失败  01-成功

校验和

5A

5:设置写地址命令

PC发送:

头部

功能

数据长度

数据

校验

结束

55 AA

05

04 00

地址(AA AA AA AA)

校验和

5A

设备返回:

头部

功能

数据长度

数据

校验

结束

55 AA

05

01   00

 00-失败  01-成功

校验和

5A

6:数据校验命令

PC发送:

头部

功能

数据长度

数据

校验

结束

55 AA

06

13 00

当前时间如2024/02/02 10:00:00

校验和

5A

设备返回:

头部

功能

数据长度

数据

校验

结束

55 AA

06

01   00

 00-失败  01-成功

校验和

5A

三:数据接收与处理

上位机采用事件驱动方式,防止界面卡顿提高升级效率。本方案使用QT5进行串口编程,实现单片机升级功能。本方案使用串口类 QSerialPort

手动拖出如下界面:

1:扫描串口实现

void MainWindow::on_ScanDevicePButton_clicked()

{

   QString UartPortName =  ui->portBox->currentText();

   ui->portBox->clear();

    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())

    {

        // 自动读取串口号添加到端口portBox中

        QSerialPort serial;

        serial.setPort(info);

        if(serial.open(QIODevice::ReadWrite))

        {

            ui->portBox->addItem(info.portName());

            serial.close();

        }

    }

    ui->portBox->setCurrentText(UartPortName);

}

2:串口写数据

void MainWindow::SerialPortWriteFrame(uint8_t func,QByteArray &data)

{

    QByteArray uartBuf;

    int checkSum=func;

    int lenght = data.size(); //

    uartBuf.resize(5);  //预留5

    uartBuf[0] = 0x55;

    uartBuf[1] = 0xAA;

    uartBuf[2] = func;

    uartBuf[3] = lenght&0xff;

    uartBuf[4] = (lenght>>8)&0xff;

    uartBuf.append(data);

    for(int i=0;i<data.size();i++)

    {

     checkSum +=  data.at(i);

    }

    uartBuf.append(checkSum&0xff);

    uartBuf.append(0x5a);

    if(SerialPort.isOpen())

  {

       SerialPort.write(uartBuf);

    }

}

3:下载状态机

1:用户触发按键开始数据发送;设置重试次数,并开启重试定时器。

2:当串有数据返回,判断是的是正响应,如果正响应继续发送数据。

3:超时定时器超时响应,对重试计算器减一,并对上次数据重新发送。

4:在发送数据这做一个判断分支就能实现发送流程。

1>数据发送流程

void MainWindow::UpdateStep(BT_program_st *program)

{

    QByteArray frameData;

    frameData.clear();

    switch(program->current_Step)

    {

    case REQ_STATUS://获取板子信息

         m_timer->start(500);

         if(program->retry_flag == false)

         {

             program->retry_Num = 2//重试5次

             program->response = RESP_STATUS;//希望返回准备命令

         }

        SerialPortWriteFrame(1,frameData);

        break;

    case REQ_JUMPBOOT://进入boot

       {

        char temp;

        m_timer->start(500);

        if(program->retry_flag == false)

        {

            program->retry_Num = 2//重试2次

            program->response = RESP_JUMPBOOT;//希望返回设置成功命令

        }

        if(program->jumpBootNumber>0)program->jumpBootNumber--;

        temp = (softVersionInfo.satrtAdr>>24)&0xff;

        frameData.append(temp);

        temp = (softVersionInfo.satrtAdr>>16)&0xff;

        frameData.append(temp);

        temp = (softVersionInfo.satrtAdr>>8)&0xff;

        frameData.append(temp);

        temp = (softVersionInfo.satrtAdr)&0xff;

        frameData.append(temp);

        SerialPortWriteFrame(2,frameData);

        }

        break;

    case REQ_ERASE_SRECTOR://擦除扇区

    {

        char temp;

        m_timer->start(1500);

        if(program->retry_flag == false)

        {

            program->retry_Num = 2//重试3次

            program->response = RESP_ERASE_CMD;//希望返回设置成功命令

        }

        program->blockIndex = 0;

        program->sendIndexCnt = 0;

        temp = (program->address>>24)&0xff;

        frameData.append(temp);

        temp = (program->address>>16)&0xff;

        frameData.append(temp);

        temp = (program->address>>8)&0xff;

        frameData.append(temp);

        temp = (program->address)&0xff;

        frameData.append(temp);

        temp = (program->lenght>>24)&0xff;

        frameData.append(temp);

        temp = (program->lenght>>16)&0xff;

        frameData.append(temp);

        temp = (program->lenght>>8)&0xff;

        frameData.append(temp);

        temp = (program->lenght)&0xff;

        frameData.append(temp);

        SerialPortWriteFrame(3,frameData);

       }

        break;

    case REQ_WRITE_DATA://发送数据

       {

        m_timer->start(700);

        if(program->retry_flag == false)

        {

            program->retry_Num = 3//重试3次

            program->response = RESP_WRITE_DATA;//希望返回设置成功命令

        }

        if(program->blockIndex<FirmwarebyteArray.size())

        {

            program->blockLenght = FirmwarebyteArray.size()-program->blockIndex;

            if(program->blockLenght>512program->blockLenght = 512;

            char * pData = FirmwarebyteArray.data();

            frameData.append((char *)(&pData[program->blockIndex]),program->blockLenght);

        }

        program->sendIndexCnt++;

        SerialPortWriteFrame(4,frameData);

        }

        break;

    case REQ_START_ADDRESS://设置写地址

        {

           char temp;

            m_timer->start(700);

            if(program->retry_flag == false)

            {

                program->retry_Num = 3//重试3次

                program->response = RESP_START_ADDRESS;//希望返回设

//成功命令

            }

            temp = (program->address>>24)&0xff;

            frameData.append(temp);

            temp = (program->address>>16)&0xff;

            frameData.append(temp);

            temp = (program->address>>8)&0xff;

            frameData.append(temp);

            temp = (program->address)&0xff;

            frameData.append(temp);

            SerialPortWriteFrame(5,frameData);

        }

        break;

    case REQ_VERIFY:

    {

        char updateTime[20]={};

        QDateTime current_date_time =QDateTime::currentDateTime();

        QString current_date =current_date_time.toString("yyyy/MM/dd hh:mm:ss");

        const char *ptime = current_date.toStdString().c_str();

        memcpy(updateTime,ptime,19);

        updateTime[19] =0;

        m_timer->start(700);

        if(program->retry_flag == false)

        {

            program->retry_Num = 3//重试3次

            program->response = RESP_VERIFY;//希望返回准备命令

        }

         frameData.append((const char *)updateTime,20);

         SerialPortWriteFrame(REQ_VERIFY,frameData);

    }

        break;

    case REQ_RUN_APP:

    {

         char temp;

         m_timer->start(500);

         if(program->retry_flag == false)

         {

             program->retry_Num = 2//重试2次

             program->response = RESP_JUMPBOOT;//希望返回设置成功命令

         }

         if(program->jumpBootNumber>0)program->jumpBootNumber--;

         temp = (softVersionInfo.satrtAdr>>24)&0xff;

         frameData.append(temp);

         temp = (softVersionInfo.satrtAdr>>16)&0xff;

         frameData.append(temp);

         temp = (softVersionInfo.satrtAdr>>8)&0xff;

         frameData.append(temp);

         temp = (softVersionInfo.satrtAdr)&0xff;

         frameData.append(temp);

         SerialPortWriteFrame(2,frameData);

     }

     break;

    case REQ_FINISH:

        if(IsSerialPortOpencloseSerialPort();

        ui->textEdit->append("程序更新完成(*^_^*) \r\n");

        program->response = RESP_NO;//密钥核对状态

        program->blockIndex = 0;

        program->sendByteNumber = 0;

        break;

    case REQ_ERROR:

         ui->textEdit->append("执行失败");

        if(IsSerialPortOpencloseSerialPort();

        break;

    }

}

2>串口数据接收流程

void MainWindow::serial_Read()

{

    //从缓冲区中读取数据

    QByteArray buffer = SerialPort.readAll();

    if(!buffer.isEmpty())//如果非空说明有数据接收

    {

        m_UartDataBuf +=  buffer;

        SerialDataAnalysis(m_UartDataBuf);

    }

}

//串口数据帧格式解析

void MainWindow::SerialDataAnalysis(QByteArray &buffer)

{

    int offset,Byteslenght;

    while(1)

    {

        offset = FindSerialDataHead(buffer);  //先清空头部多余的数据

        if(offset)buffer.remove(0,offset);

        Byteslenght = buffer.size();

        if(Byteslenght >= 7)

        {

          unsigned char func;

          unsigned int  lenght;

          unsigned char end;

          func    = buffer[2];

          lenght =  (unsigned char)buffer[4];

          lenght <<= 8//低在前

          lenght |= (unsigned char)buffer[3];

          if(lenght>1024)

          {

              buffer.remove(0,2);

              qDebug()<<"数据长度异常";

              continue;

          }

          if(Byteslenght>=(int)(lenght+7))

          {

             end = buffer[lenght+6];

             if(end ==0x5a)

             {

               QByteArray text =   buffer.left(lenght+7);

               buffer.remove(0,lenght+7);

               SerialFrameAnalysis(text);  //数据响应处理

             }else

             {

               buffer.remove(0,lenght+7);

               qDebug()<<"数据帧异常";

             }

          }

          else

          {

              break;

          }

        }

        else

        {

           break;

        }

    }

}

// 用户数据解析

void MainWindow::SerialFrameAnalysis(QByteArray &buffer)

{

   char response = buffer.at(2);

   if(m_program_Data.response == response)

   {

       m_program_Data.retry_flag  = false//不需要重试了

       m_timer->stop();

       //获取单片机运行状态  0->单片机在底层  1->单片机在应用层

       if(m_program_Data.current_Step ==REQ_USER_INFO)

       {

           FW_INFO fwInfo;

           char *pcharbuffer.data();

           memcpy(&fwInfo,&pchar[5],sizeof(FW_INFO));

           FirmwareInfoDisplay(fwInfo);

           closeSerialPort();

           //qDebug()<<"REQ_USER_INFO";

       }

      else if(m_program_Data.current_Step == REQ_STATUS)//

      {

          FW_INFO fwInfo;

          char *pcharbuffer.data();

          memcpy(&fwInfo,&pchar[5],sizeof(FW_INFO));

          FirmwareInfoDisplay(fwInfo);

          if(memcmp(fwInfo.Functions,"bootloader",10)==0)

          {

                       if(memcmp(fwInfo.FirmwareName,firmwareInfo.FirmwareName,strlen(firmwareInfo.FirmwareName)))

            {

                //固件名字问题

                ui->textEdit->append("警告!   固件与产品不匹配");

                m_program_Data.current_Step = REQ_ERROR;//烧写错误

            }

            else

            {

            m_program_Data.current_Step = REQ_ERASE_SRECTOR;//扇区擦除

            }

            UpdateStep(&m_program_Data);

          }

          if(memcmp(fwInfo.Functions,"App",4)==0)

          {

            qDebug()<<"APP";

            if(m_program_Data.jumpBootNumber>0)

            {

              m_program_Data.current_Step = REQ_JUMPBOOT;//进入底层

            }

            else

            {

              m_program_Data.current_Step = REQ_ERROR;

            }

            UpdateStep(&m_program_Data);

          }

      }

      else if(m_program_Data.current_Step == REQ_JUMPBOOT)

      {

          char status = buffer.at(5);

          if(status)

          {

              m_program_Data.current_Step = REQ_STATUS;//扇区擦除

          }

          else

          {

              m_program_Data.current_Step = REQ_ERROR;

              ui->textEdit->append("错误! 无法进入底层 ");

          }

          UpdateStep(&m_program_Data);

      }

      else if(m_program_Data.current_Step == REQ_ERASE_SRECTOR)

      {

          char status = buffer.at(5);

          if(status)

          {

            m_program_Data.current_Step = REQ_START_ADDRESS;//扇区擦除

          }

          else

          {

              ui->textEdit->append("错误! flash 擦除出错 ");

              m_program_Data.current_Step = REQ_ERROR;//失败

          }

          UpdateStep(&m_program_Data);

      }

      else if(m_program_Data.current_Step ==REQ_START_ADDRESS)

      {

          char status = buffer.at(5);

          if(status)

          {

              m_program_Data.current_Step = REQ_WRITE_DATA;//扇区擦除

          }

          else

          {

              ui->textEdit->append("错误! 设置写地址失败 ");

              m_program_Data.current_Step = REQ_ERROR;//失败

          }

          UpdateStep(&m_program_Data);

      }

      else if(m_program_Data.current_Step ==REQ_WRITE_DATA)//写数据

      {

         char status = buffer.at(5);

         if(status)

         {

             QTextCursor cursor = ui->textEdit->textCursor();

             cursor.movePosition(QTextCursor::End);

             ui->textEdit->setTextCursor(cursor);

             ui->textEdit->insertPlainText("#");

             m_program_Data.blockIndex += m_program_Data.blockLenght;

             m_program_Data.blockLenght = 0;

             if(m_program_Data.blockIndex>=FirmwarebyteArray.size())

             {

              m_program_Data.current_Step = REQ_VERIFY;//校验数据

             }

             else

             m_program_Data.current_Step = REQ_WRITE_DATA;//继续写数据

         }

         else

         {

             ui->textEdit->append("错误! 写FLASH失败 ");

             m_program_Data.current_Step = REQ_ERROR;//失败

         }

         UpdateStep(&m_program_Data);

      }

       else if(m_program_Data.current_Step == REQ_VERIFY )//

       {

           char status = buffer.at(5);

           if(status)

           {

            m_program_Data.current_Step = REQ_RUN_APP;//校验数据

             ui->textEdit->append("校验 正确");

           }

           else

           {

            m_program_Data.current_Step = REQ_ERROR;//继续写数据

             ui->textEdit->append("错误! 校验失败 ");

           }

            UpdateStep(&m_program_Data);

       }

       else if(m_program_Data.current_Step == REQ_RUN_APP )

       {

           char status = buffer.at(5);

           if(status)

           {

            m_program_Data.current_Step = REQ_FINISH;//校验数据

             ui->textEdit->append("运行 正确 ");

           }

           else

           {

            m_program_Data.current_Step = REQ_ERROR;//继续写数据

             ui->textEdit->append("运行 失败 ");

           }

            UpdateStep(&m_program_Data);

       }

      else if(m_program_Data.current_Step == REQ_ERROR )//REQ_SECURITY_SEED

      {

      }

   else if(m_program_Data.current_Step ==REQ_FINISH)//判断密码是否正确

      {

         //烧写完成

           m_program_Data.current_Step = REQ_STATUS;

           m_program_Data.response = RESP_NO;

      }

   }

}

3>定时器超时处理流程

void MainWindow::UpdateOverTime()

{

    if(m_timer->isActive())//如果定时器已经开启就关闭

    {

        m_program_Data.retry_flag = true;

        this->m_timer->stop();

        if(m_program_Data.retry_Num>0)

        {

          m_program_Data.retry_Num--;

        }

        else

        {

            m_program_Data.current_Step = REQ_ERROR;

            m_program_Data.response = RESP_NO;

        }

         UpdateStep(&m_program_Data);

    }

}

 

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值