Thread 中讨论的其他器件: UNIFLASH、 SYSCONFIG
工具/软件:
通过串行端口升级 CC2340R5 蓝牙固件时、即使之前闪存擦除和写入操作成功、该过程在 CRC 验证步骤中也会失败。 应如何解决此问题?

This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
尊敬的 Mike:
使用 TI_CC2340_Linux_SBL 工具时、必须确保 创建自定义 bin 文件 、并确保固件二进制文件与扇区大小 (0x800) 对齐。 映像的“Byte Count“(0x64db4) 意味着未遵守这些指令。
此致、
Ryan
您好、TI 工程师
我目前有以下问题:
。 ccfg 文件 已通过导出 Uniflash 工具 从地址开始、 0x4e020000 到地址结束 0x4e0207ff。 无法将其导出到 0x4e020800。 如何使其兼容?
在下面的屏幕截图中、 out 从开始的文件大小 数据 如图所示。 当我们刻录文件时、该过程在开始写入闪存时失败。 在实际计算后、文件大小不是 2048 字节的整数倍。 应该如何配置? 请提供指导。
在下面的屏幕截图中、 out 从开始的文件大小 UART 如图所示。 当我们刻录文件时、进程在期间失败 写入闪存后的 CRC 校验 。 在实际计算后、文件大小不是 2048 字节的整数倍。 应该如何配置? 请提供指导。
指向的链接 TI_CC2340_Linux_SBL 工具 无法访问您的公司提供的信息。 请您再次提供链接吗?


1.0x4e0207ff 是 CCFG 的最后一个字节、应该是从 Uniflash 中读取 CCFG 的最后一个字节
2.不应将.out 文件与 Linux SBL 工具一起使用、因为它只接受二进制文件。 您应该从与 0x800 扇区大小对齐的 Uniflash 中以二进制格式读取存储器。 这将需要使用.out 文件对 CC2340R5 器件进行一次编程(不必是最终目标)
4.如果您无法访问 GitHub ,则附加副本
e2e.ti.com/.../TI_5F00_CC2340_5F00_Linux_5F00_SBL.zip
此致、
Ryan
您好、Ryan、
我有几个问题需要与您确认:
谢谢!
请务必阅读 TRM 的第 8 章(器件引导和引导加载程序)。

此致、
Ryan
1.可以从 SysConfig 中选择“User Specific Bootloader“及其引导加载程序矢量表。 加密 TI 驱动程序 和 示例 可供您参考、但目前 TI 并未公开提供全功能自定义引导加载程序解决方案。 
2. TRM 的第 8 章(器件引导和引导加载程序)全面介绍了所有引导 加载程序命令、便于用户完全操作 ROM 串行引导加载程序、以升级其应用中的器件。
3. CRC32(循环冗余校验 32)是 一种 32 位错误检测代码、可从数据块生成固定长度校验和以验证其完整性。
此致、
Ryan
尊敬的 Mike:
我很抱歉、因为我认为我造成了一些我需要纠正的困惑。 空白 CCFG 设置仅对应于空白器件、由于您要对新的闪存存储器进行编程、因此还需要对 CCFG 进行编程以保持匹配。 您无法继续使用以前的 CCFG 设置、因为它们已通过引导加载程序擦除命令失效和擦除。 我已经修改了我以前的答复。 为了进行清除、预期的 ROM 串行引导加载程序过程期望闪存和 CCFG 内容都将被写入、因为在序列开始时必须擦除这两个内容。
此致、
Ryan
您好、Ryan
我还有几个问题:
1.通过串行端口升级时、是否对 CCFG 和蓝牙固件的编程顺序有严格的要求? 在固件之前对 CCFG 进行编程是否可以接受?
2.目前、我们的主机对闪存进行编程的默认波特率是 115200。 如果我们将其更改为另一个值、则升级失败。 如果我们要将其更改为 921600 以缩短擦除和编程时间、究竟应该在哪里修改? 此外、使用主机计算机对固件进行编程时可以设置的最大波特率是多少、在最大波特率下的近似误码率是多少?
3.在对 CCFG 区域进行非初始升级期间、主机计算机程序是否可以设置为不擦除或重写 CCFG、并继续使用以前版本的 CCFG 文件? 还是在引导加载程序中实现、导致我们无法修改?
谢谢!
1.虽然 TRM 中没有任何内容表明您无法或不应这样做、但 CCFG 是最后编程的闪存更有意义、因为它会验证 ROM 串行引导加载程序的完整性。 否则、如果在固件编程期间进行复位或下电上电并且 CCFG 已经完成、则可能会导致器件启动至不完整的固件应用程序。
2.以下是来自 TRM 的摘录。 我不知道您为什么发现 921600 无效、因为这应该是一个可接受的值。 您能否澄清一下到底是什么失败、以及它是如何指示的? 460800 是否成功? 如果使用定制硬件、则可能需要检查 UART 线路的完整性。 任何位错误都应通过 CRC 校验捕获。
8.5.1.2.1.1 UART 波特率自动检测 引导加载程序提供了一种自动检测用于与其通信的 UART 波特率的方法。 若要与主机同步、引导加载程序必须接收值为 0x55 的 2 个字节。 如果同步成功、引导加载程序将返回一个由 2 个字节组成的确认、其值为 0x00 和 0xCC。 如果同步失败、引导加载程序会等待同步尝试。 在自动检测功能中、使用 GPIO 中断来监测 UART0 RX 引脚是否存在边沿。 当检测到足够的边沿时、引导加载程序会确定对 UART 进行编程所需的波特率和频率之比。 UART 模块系统时钟必须至少为波特率的 16 倍;因此、最大波特率不能高于 3MBaud (48MHz 除以 16)。 由于固件功能能够检测主机的传输速率、因此最大波特率限制为 1.6Mbaud。
3.唯一的引导加载程序擦除命令包含 CCFG 的擦除操作、因此如果更新应用程序、还必须对 CCFG 进行编程。 您可能会像以前一样对 CCFG 内容进行编程。
此致、
Ryan
您好、Ryan
以下是我的两个问题:
1.目前只有 115200 的波特率可用。 其他波特率将导致波特率不匹配错误。 `屏幕截图中所示的代码、MCU 似乎没有返回` 0x00、0xCC `或` 0x00、0x33 μ s、这会导致问题。 这些返回值是硬编码的、还是我可以自行修改?
2.在 CCS 工具中、有一个用于设置波特率的选项、但该选项灰显且无法修改、如屏幕截图所示。 我可以自行修改此设置吗? 如果是、我该如何操作?


谢谢!
TI_CC2340_Linux_SBL 工具支持多种波特率设置。 您如何更改波特率以及您是否使用逻辑分析仪或示波器确认了输出 TX。
2.您正在查看 SysConfig 文件中的示例应用程序、该示例应用程序不适用于串行 ROM 引导加载程序功能。
从 TI_CC2340_Linux_SBL 自述文件中:
串行引导加载程序提供预编译的 sbl.out 文件、该文件可用于运行预编译的映像。
为了优化应用程序的引导加载程序、可以更改 Source 文件夹中提供的 C 代码。
SBL 主要由一组.c 和.h 文件组成:
这些文件可用于使引导加载程序适应自定义应用程序。
示例更改如下:
调整波特率:这可以按照以下步骤完成:
若要在更改后编译工程、可以使用 TI_CC2340_Linux_SBL 文件夹中提供的 makefile。 这可以通过从 Linux 终端调用“make"来“来实现。
此致、
Ryan
您好、Ryan
虽然我们查看了数据表并发现可以自动检测到 9600 和 1.6Mbaud 之间的波特率、但在调试过程中只能检测和正常使用屏幕截图中显示的波特率。 是否还没有配置其他更高的波特率? 此外、如屏幕截图所示、当波特率加倍时、为什么编程时间不会减少一半?
此外、在写入闪存时、为什么每次写入操作所用的总时间不同? 这种现象的原因可能是什么?
当波特率为 460800 时、编程时间超过 100 秒。 这是否异常? 造成这种情况的具体原因是什么?


谢谢!
您好、Ryan
以下是您问题的翻译:
1.我发现波特率 460800 时,串行通信的标准传输速度约为 30 到 40 秒。 然而,在我自己的测试中,它已经超过了 100 秒。 为何存在如此大的差异?
2.如果写入闪存和执行闪存命令的过程是静态的,当波特率加倍时,合适的下载速度是多少?
3.在我的测试中,我可以使用的最大波特率是 460800。 超出此范围时、会发生失配误差。 您提到、主机的波特率生成可能存在问题。 具体来说、这可能是什么问题? 我们是否可以对其进行修改以提高波特率?
4、使用相同波特率时,为什么连续刷写内存时,每次尝试刷新内存所需的时间会有所不同?
谢谢!
1.您是否每次使用相同的二进制映像进行编程,二进制文件的大小是否有变化? 是否确定不存在导致重新启动过程的 CRC 不匹配?
2.我没有运行任何测试,但我预计它将是线性的二进制图像大小和波特率
3.您的硬件(TI EVM,定制设计等)如何才能提供示波器或逻辑分析仪对失败的波特率检测过程进行测量?
4.我不能确定这是没有进一步证据的情况。 “连续“表示您是指在每次重新启动到引导加载程序期间、还是在相同的引导加载程序序列中多次刷写器件?
此致、
Ryan
您好、Ryan
1.在调试过程中,我使用了相同大小的相同二进制映像,在整个刷写过程中没有发生 CRC 校验和故障。
2.在我的自检中,当使用相同的二进制映像和不同的波特率时,刷新时间随着波特率在可用范围内的增加而减少。 但是、总完成时间仍然没有达到预期的绩效。
3.我使用 CC2340R5RGE 蓝牙 MCU。 由于电流限制、我目前无法提供相应的测量数据。
4.表示“连续“、是指每次重新输入引导加载程序以进行串行刷写时。
谢谢!
尊敬的 Mike:
我使用 TTL232R-3V3 FTDI 至 USB 电缆进行测试、因为 XDS110 不应与该工具一起使用、并能够 使用完整闪存 512KB 映像 (0x7FFFF) 实现 406800 和 921600 两种波特率。 如果我仅在 Uniflash 中读取从 0x0 到 0x3FFFF 的闪存(根据没有 NVS 区域的输出*。map 文件、我的 BLE 应用有足够的闪存空间,因此更新后不会恢复保存的连接)、则使用 406800 波特率的时间会从 70 秒减少到 37 秒。 如果我按照自述文件中的说明、在 sbl_device_cc2340.c 中进一步移除 setProgress 并将 bExpectAck 设置为 false、则时间进一步缩短至 20s。
此致、
Ryan
您好、Ryan
我已根据您的建议进行了修改、但我仍有以下问题:
setProgress 并 bExpectAck false 在 sbl_device_cc2340.c 文件中删除并设置后、编程时间减少到大约 35 秒、波特率为 460800。 但是、它仍然无法达到您提到的 20 秒。 这可能是什么原因? sbl_device_cc2340.c 下面提供了修改后的 Linux_Serial 文件的源代码。 您能帮助我检查一下与您提供的演示代码相比有哪些差异吗? 我的意思是、为什么相同的代码仍然会导致额外的 15 秒编程时间? 此外、此文件包含我们自己配置的波特率参数、但 921600 仍然无法使用。 您能帮助检查参数配置是否有任何问题吗? /*sbl_device_cc2340.c write flash*/
tSblStatus writeFlashRange(uint32_t ui32StartAddress,
uint32_t ui32ByteCount, const char *pcData)
{
uint32_t devStatus = CMD_RET_UNKNOWN_CMD;
tSblStatus retCode = SBL_SUCCESS;
char u8tCmdStatus[1];
uint32_t bytesLeft, dataIdx, bytesInTransfer,u32tStatusLength=1;
uint32_t transferNumber = 1;
bool bACK=true;
bool bBlToBeDisabled = false;
tTransfer pvTransfer[2];
uint32_t ui32TotChunks = (ui32ByteCount / SBL_CC2340_MAX_BYTES_PER_TRANSFER);
uint32_t ui32CurrChunk = 0;
if(ui32ByteCount % SBL_CC2340_MAX_BYTES_PER_TRANSFER) ui32TotChunks++;
/*Init transfer*/
pvTransfer[0].bExpectAck = false; /*Check if operation successfull?*/
pvTransfer[0].byteCount = ui32ByteCount;
pvTransfer[0].startAddr = ui32StartAddress;
pvTransfer[0].startOffset = 0;
/* For each transfer */
for(uint32_t i = 0; i < 1; i++)
{
/* Sanity check */
if(pvTransfer[i].byteCount == 0)
continue;
/* Set progress */
//setProgress(addressToPage(pvTransfer[i].startAddr));
/* Send download command */
if((retCode = cmdDownload(pvTransfer[i].startAddr,
pvTransfer[i].byteCount)) != SBL_SUCCESS)
return (retCode);
/* Check status after download command */
retCode = readStatus(&devStatus);
if(retCode != SBL_SUCCESS)
{
printf("Error during download initialization. Failed to read device status after sending download command.\n");
return (retCode);
}
/* Send data in chunks */
bytesLeft = pvTransfer[i].byteCount;
dataIdx = pvTransfer[i].startOffset;
while(bytesLeft)
{
/* Set progress */
//setProgress( ((100*(++ui32CurrChunk))/ui32TotChunks) );
/* Limit transfer count */
bytesInTransfer = MIN(SBL_CC2340_MAX_BYTES_PER_TRANSFER, bytesLeft);
/* Send Data command */
if((retCode = cmdSendData((const uint8_t*)&pcData[dataIdx], bytesInTransfer)) != SBL_SUCCESS)
{
printf("Error during flash download. \n- Start address 0x%08X (page %d). \n- Tried to transfer %d bytes. \n- This was transfer %d.\n",
(ui32StartAddress+dataIdx),
addressToPage(ui32StartAddress+dataIdx),
bytesInTransfer,
(transferNumber));
return (retCode);
}
if(pvTransfer[0].bExpectAck){
if((retCode=sendCmd(CMD_GET_STATUS,NULL,0))!=SBL_SUCCESS){
printf("Sending GET_STATUS_CMD failed\n");
return(SBL_ERROR);}
/* Receive command response (ACK/NAK) */
if((retCode = getCmdResponse(&bACK, 5)) != SBL_SUCCESS){
printf("Device NAKed Status CMD command\n");
return (retCode);}
if(retCode = getResponseData((uint8_t*)u8tCmdStatus,&u32tStatusLength,0) != SBL_SUCCESS){
sendCmdResponse(false);
return (retCode);}
sendCmdResponse(true);
if(u8tCmdStatus[0]!=0x40){
printf("Error during flash download. \n- Start address 0x%08X (page %d). \n- Tried to transfer %d bytes. \n- This was transfer %d.\n",
(ui32StartAddress+dataIdx),
addressToPage(ui32StartAddress+dataIdx),
bytesInTransfer,
(transferNumber));
return (SBL_ERROR);
}
}
bytesLeft -= bytesInTransfer;
dataIdx += bytesInTransfer;
transferNumber++;
}
}
return (SBL_SUCCESS);
}
/*Linux_Serial.c SetBaudParm*/
static void setBaudRate(int baud)
{
switch(baud)
{
case 38400:
/*Set Baudrate to 38400 Baud*/
cfsetispeed(&SerialPortSettings,B38400);
cfsetospeed(&SerialPortSettings,B38400);
printf("\n BaudRate = 38400\n");
break;
case 57600:
/*Set Baudrate to 57600 Baud*/
cfsetispeed(&SerialPortSettings,B57600);
cfsetospeed(&SerialPortSettings,B57600);
printf("\n BaudRate = 57600\n");
break;
case 230400:
/*Set Baudrate to 230400 Baud*/
cfsetispeed(&SerialPortSettings,B230400);
cfsetospeed(&SerialPortSettings,B230400);
printf("\n BaudRate = 230400\n");
break;
case 460800:
/*Set Baudrate to 460800 Baud*/
cfsetispeed(&SerialPortSettings,B460800);
cfsetospeed(&SerialPortSettings,B460800);
printf("\n BaudRate = 460800\n");
break;
case 9600:
/*Set Baudrate to 9600 Baud*/
cfsetispeed(&SerialPortSettings,B9600);
cfsetospeed(&SerialPortSettings,B9600);
printf("\n BaudRate = 9600\n");
break;
case 1000000:
/*Set Baudrate to 1000000 Baud*/
cfsetispeed(&SerialPortSettings,B1000000);
cfsetospeed(&SerialPortSettings,B1000000);
printf("\n BaudRate = 1000000\n");
break;
case 2000000:
/*Set Baudrate to 2000000 Baud*/
cfsetispeed(&SerialPortSettings,B2000000);
cfsetospeed(&SerialPortSettings,B2000000);
printf("\n BaudRate = 2000000\n");
break;
case 115200:
/*Set Baudrate to 115200 Baud*/
cfsetispeed(&SerialPortSettings,B115200);
cfsetospeed(&SerialPortSettings,B115200);
printf("\n BaudRate = 115200\n");
break;
case 921600:
/*Set Baudrate to 921600 Baud*/
cfsetispeed(&SerialPortSettings,B921600);
cfsetospeed(&SerialPortSettings,B921600);
printf("\n BaudRate = 921600\n");
break;
case 1152000:
/*Set Baudrate to 1152000*/
cfsetispeed(&SerialPortSettings,B1152000);
cfsetospeed(&SerialPortSettings,B1152000);
printf("\n BaudRate = 1152000\n");
break;
case 1500000:
/*Set Baudrate to 1500000*/
cfsetispeed(&SerialPortSettings,B1500000);
cfsetospeed(&SerialPortSettings,B1500000);
printf("\n BaudRate = 1500000\n");
break;
default:
printf("ERROR: INVALID BAUD\n");
break;
}
}
/****************************************************************
* Function Name : configPort
* Description : Populate the termios structure
* Returns : None
* Params @None
****************************************************************/
void configPort(int baudspeed)
{
/*Using thermios structure to set Serial Port structure*/
memset(&SerialPortSettings, 0, sizeof(SerialPortSettings));
setBaudRate(baudspeed);
/*Configure serial port message interpretation */
/* Enable receiver*/
SerialPortSettings.c_cflag |= (CLOCAL | CREAD);
/* Set Data Size= 8Bit */
SerialPortSettings.c_cflag &= ~CSIZE;
SerialPortSettings.c_cflag |= CS8;
/* No Parity */
SerialPortSettings.c_cflag &= ~PARENB;
/* 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSTOPB;
SerialPortSettings.c_cflag &= ~CRTSCTS;
/* setup for non-canonical mode */
SerialPortSettings.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
SerialPortSettings.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
SerialPortSettings.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
SerialPortSettings.c_cc[VMIN] = 0;
SerialPortSettings.c_cc[VTIME] = 2;
/* Flush out if there is any previously pending **** */
clearRxbuffer();
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0)
perror("ERROR in Setting attributes |");
else
printf(" StopBits = 1 \n Parity = none\n");
}
您好、Ryan
我还有两个问题。
*问题 1:**
如 μ`main.c`文件的屏幕截图所示:
```c
Uint16_t CcfgRegionStart[4]={0x0000、0x0010、0x0750、0x07D0};
Uint16_t CcfgRegionEnd[4]={0x000B、0x074B、0x07CB、0x07FB};
```μ s
关于`cfg`文件的起始地址和结束地址、在函数`fseek (CCFGfPtr、regionEndAddr、seek_set)`中、偏移量是否需要将 1 添加到结束地址? 否则、下一个写入操作可能会直接覆盖结束地址处的字节。 此外、如何以这种方式写入时确保 CRC 校验和值的准确性?
*问题 2:**
在函数`fwrite (&Crc32Value[0]、1、4、CCFGfPtr)`中、在写入过程中、如果发生电源故障或设备重新启动、文件是否会损坏? 是否可以将其修改为直接从存储器写入?


谢谢!
在函数`fwrite (&Crc32Value[0]、1、4、CCFGfPtr) 中;`、在写入过程中、如果发生电源故障或设备重新启动、文件是否会损坏? 是否可以将其修改为直接从内存写入?
行为取决于相关的 CRC32: Ccfg.bootcfg.CRC32、Ccfg.CRC32、 Ccfg.userRecord.crc32、 Ccfg.debugCfg.crc32。 如果 器件在重新启动时 Ccfg.bootcfg.CRC32 不正确、则会禁用引导加载程序、然后会出现 TRM 中提到的引导配置问题。 否则、可以通过重新执行引导加载程序刷写序列来恢复这种情况。
此致、
Ryan
尊敬的 Mike:
这不是一个典型的观察结果 、因为 SimpleLink Connect 应用 适用于 Android 或 iOS、适用于 basic_ble 或 data_stream 项目。 您需要提供有关项目变更和 iOS 版本的更多详细信息、但我要求您在这样做时发布新的 E2E 帖子、因为此当前主题涵盖了不同的主题(ROM 串行引导加载程序)。
此致、
Ryan