SAF775C 是恩智浦半导体 (NXP) 将汽车收音机和音响系统完全集成在单个芯片上的 IC ,是恩智浦广受欢迎的汽车收音机和音频 DSP 产品系列的第三代。SAF775C 的固件升级是通过上位机工具进行升级 ,常用到的升级是对音频 DSP 部分进行升级 ,在机器做成成品后通过上位机升级会非常麻烦 , 我们接下来要分享得部分是如何在 iMX8系统中对 SAF775C 的固件部分进行升级 ,以下内容部分由 Pual 和 Watson 共同完成 ,Watson 进行整理 ,调试部分由 Baker 和 Watson 现场调试 ,调试结果得到客户确认 !
二. 环境搭建
2.1 Flash 的烧录流程 :
图1. FLASH 烧录流程
2.2 确认 i.MX8 和 SAF775C 之间的通信已经打通
SAF775C 支持 I2C 通信和 SPI 通信 ,我们目前使用的是 SPI 接口和SAF775C 进行通信和数据传输
2.3 D3DL.cpp 文件 :
D3DL.cpp 这个文件是运行在上位机升级工具的源文件 ,请找原厂或是供应商提供 ,接下来我们会把下载驱动部分移植到 Android 平台
三. SAF775C升级固件程序移植
3.1 加载升级固件
原厂提供的代码是在 Windows 上执行的文件打开读写操作,需要将对应的操作函数修改为可在 Android 平台上执行的函数,否则编译会提示找不到对应函数导致编译失败
对以下内容修进行修改:
static void D3DL_LoadImage(unsigned char nUsbUnitId) { CFile spi_binaryfile; uint8_t i; …………………………… if (spi_binaryfile.Open(pImageName[i], CFile::modeRead)) //打开文件操作 { filesize = (unsigned long)spi_binaryfile.GetLength(); //获取文件大小 cnt = spi_binaryfile.Read(image, 4); // 读取文件前四个字节内容 …………………………………………………… cnt = fread(&image[4], 1, filesize-4, fp); // 读取文件剩余内容 …………………………………………………… spi_binaryfile.Close(); // Close file |
对以上的内容修改为:
static void D3DL_LoadImage(unsigned char nUsbUnitId) { uint8_t i; ……………………………………… fp = fopen(pImageName[i], "r"); // 打开文件操作 // 获取文件大小 fseek(fp, 0, SEEK_END); filesize = ftell(fp); // 读取文件前四个字节内容 fseek(fp, 0, SEEK_SET); cnt = fread(image, 1, 4, fp); ……………………………………… cnt = fread(&image[4], 1, filesize-4, fp); //读取文件剩余内容 fclose(fp); // Close file |
D3DL_LoadFlashImage()函数关于文件加载部分的修改 ,可以参考上面内容修改
3.2 升级固件传输 :
打开的 Binary 文件需要通过 IOCTL 的方式传递给驱动层 ,驱动层接收数据后通过 SPI 总线把数据写入到 SAF775C Flash 中
3.2.1 应用层需要对驱动层进行数据传输 ,状态读取和 Reset 操作 , 下面是对 IOCTL 功能进行定
struct saf_data { unsigned int len; unsigned char data[32]; void *buf; }; #define SAF775D_IOCTL_MAGIC 'D' #define IOCTL_D3DL_I2CSPI_WRITE_DATA _IOW(SAF775D_IOCTL_MAGIC, 0xA0, struct saf_data) #define IOCTL_D3DL_I2CSPI_READ_DATA _IOW(SAF775D_IOCTL_MAGIC, 0xA1, struct saf_data) #define IOCTL_D3DL_I2CSPI_RESET _IOW(SAF775D_IOCTL_MAGIC, 0xA2, int) #define IOCTL_D3DL_I2CSPI_READ_INT _IOW(SAF775D_IOCTL_MAGIC, 0xA3, int) |
3.2.2 对 I2CSPI_Read 和 I2CSPI_Write 函数进行封装
m_pPort->I2CSPI_ReadINT(nUsbUnitId) ,m_pPort->I2CSPI_Write(nUsbUnitId, msg, Cnt),
m_pPort->I2CSPI_Read(nUsbUnitId,msg, Cnt) ,函数进行封装 ,使它们具备原有函数读取状态 ,传递参数的功能
nUsbUnitId 这个变量是 PC 端 USB 转串口时用到的 ID ,在 Android 平台不会使用到 ,在封装函数的时候把这个选项去掉了
① m_pPort->I2CSPI_ReadINT(nUsbUnitId) 函数的封装
unsigned char Wpi_I2CSPI_ReadINT(void){ unsigned char status = 0; ioctl(fd, IOCTL_D3DL_I2CSPI_READ_INT,&status); return status; } |
② m_pPort->I2CSPI_Write(nUsbUnitId, msg, Cnt) 函数的封装
unsigned char Wpi_I2CSPI_Write_Data(unsigned char *Msg, unsigned int Lens){ struct saf_data ImageData; ImageData.len = Lens; ImageData.buf = Msg; ioctl(fd, IOCTL_D3DL_I2CSPI_WRITE_DATA, &ImageData); return 0; } |
③ m_pPort->I2CSPI_Read(nUsbUnitId,msg, Cnt) 函数的封装
unsigned char Wpi_I2CSPI_Read_Data(unsigned char *Msg, unsigned int Lens) { unsigned char i; struct saf_data sMsg; sMsg.len = Lens;
ioctl(fd, IOCTL_D3DL_I2CSPI_READ_DATA, &sMsg); for (i = 0 ;i < sMsg.len; i++) { Msg[i]=sMsg.data[i]; }
return 0; } |
④ Reset()函数
static void D3DL_Reset(void) { ioctl(fd, IOCTL_D3DL_I2CSPI_RESET, 0); } |
3.2.3 驱动部分 IOCTL 的操作
① Wpi_I2CSPI_ReadINT()对应驱动的IOCTL_D3DL_I2CSPI_READ_INT 操作
case IOCTL_D3DL_I2CSPI_READ_INT: int gpio4_status; if (copy_from_user(&gpio4_status, args, sizeof(gpio4_status))) return ERROR; gpio4_status = gpio_get_value(saf775d_GPIO4); copy_to_user((void *)args, &gpio4_status, sizeof(gpio4_status)); break; |
② Wpi_I2CSPI_Write_Data()对应驱动的IOCTL_D3DL_I2CSPI_WRITE_DATA 操作
case IOCTL_D3DL_I2CSPI_WRITE_DATA: safdbgprt(KERN_INFO"saf775d %s cmd:SAF775D_SPI_WRITE \n",__func__); struct saf_data write_data; u8 *temp_flash; int i = 0; if (copy_from_user(&write_data, (void *)args, sizeof(write_data))) return ERROR; temp_flash = (u8 *)kmalloc(write_data.len * sizeof(u8),GFP_KERNEL); if (copy_from_user((u8 *)temp_flash, (u8 *)write_data.buf, write_data.len)) return ERROR; saf775drv_write(temp_flash, write_data.len); // 把数据写到 SAF775C kfree(temp_flash); break; |
③ Wpi_I2CSPI_Read_Data()对应驱动的IOCTL_D3DL_I2CSPI_READ_DATA 操作
case IOCTL_D3DL_I2CSPI_READ_DATA: safdbgprt(KERN_INFO"saf775d %s cmd:SAF775D_SPI_READ \n",__func__); struct saf_data read_data; if (copy_from_user(&read_data, (void *)args, sizeof(read_data))) return ERROR; saf775drv_read(read_data.data, read_data.len); // 从 SAF775C 读取状态 printk("[baker_k]spi_read_finished \n"); copy_to_user((void *)args, &read_data, sizeof(read_data)); break; |
3.3 Firmware App 编译
2.3.1完成了固件代码的移植后,接下来就是对代码进行编译,生成 Android 可执行文件。
需要创建 Android.mk 文件,代码如下 :
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) LOCAL_SRC_FILES := imx8_775C_flash.c #LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LIBDRM_IMX)/libdrm-imx/ $(LIBDRM_IMX)/libdrm-imx/include/drm/ LOCAL_MULTILIB := both LOCAL_CFLAGS += -DBUILD_FOR_ANDROID LOCAL_SHARED_LIBRARIES := libutils libc LOCAL_STATIC_LIBRARIES := libdrm LOCAL_MODULE := imx8_775C_flash LOCAL_MODULE_STEM_32 := imx8_775C_flash_32 LOCAL_MODULE_STEM_64 := imx8_775C_flash_64 LOCAL_MODULE_TAGS := tests LOCAL_VENDOR_MODULE := true include $(BUILD_EXECUTABLE) |
3.3.2 将 imx8_775C_flash.c 和 Android.mk 文件放到Android SDK 新建目录下:
android_build/external/775C ,进入 android_build 目录下执行如下命令开始编译:
source build/envsetup.sh mmm external/775C |
3.3.2 编译完成后会生成对应的可执行文件:out/target/product/mek_8q/vendor/bin/imx8_775C_flash_64
图2. imx8_775C_flash_64 编译结果
四. 固件烧录调试
4.1 固件烧录参数
4.1.1 以 load_app_binary_image 的命令为例进行解释
$ imx8_775C_flash_64 -L 6 SAF775C_ABB_E7A0_nxp_pro_encrypted.bin 0 |
imx8_775C_flash_64 调用上面生成的 SAF775C Firmware Upgrade 应用程序
-L 表示 : firmware image loading
6 表示 : 6-> ICC ARM LOWER image
0 表示 : address offset是0
-R 表示 : boot the target
4.1.2 通过应用程序执行imx8_775C_flash_64 -L 6 SAF775C_ABB_E7A0_nxp_pro_encrypted.bin 0 命令后可以看到 file size = 37212
图3. 获取 file size
4.1.3 在驱动层IOCTL_D3DL_I2CSPI_WRITE_DATA 插入 log 信息 ,可以看到传输的数据长度为 0 – 37211
U8 *ImageData = temp_flash; for (i =0; i < write_data.len ; i++ ) printk("ImageData[%d] = 0x%02X", i,ImageData[i]); |
图4. ImageData 长度
通过以上信息可以确认升级文件已经成功导入到驱动层
4.2 固件烧录程序调试
4.2.1 每次对 SAF775C 进行读写操作后都要读取 GPIO4 的状态 ,确认为高后再进行下一步操作
bool Gpio4Status = Wpi_I2CSPI_ReadINT(); if (Gpio4Status == false) { // SAF775C Busy } |
4.2.2 下面是SAF775C 驱动部分烧录 Flash 的 Source Code
Load SAF775C_Arm_Ffs_nxp_pro_encrypted.bin , Boot 成功后SAF775C 才能正常运行并接收 Erase ,Flash 指令
int download_Flash_Image_all(void) { int ret; // reset saf775d reset_saf775d_gpio_long(); // host boot, load APP ret = load_app_binary_image("Dione_Arm_Ffs_nxp_pro_encrypted.bin"); if (ret != 0) return ret; msleep(10); //boot from host ret = boot_target(); if(ret != 0) return ret; msleep(30); ups.percent = 5;//finish 5% note_percent(); //erase Flash of SAF775X ret = erase_flash(); if(ret != 0) return ret; ups.percent = 10;//finish 10% note_percent(); |
4.3 使用脚本升级
为了方便升级 ,可以参考下面的脚本方式进行升级
#!/bin/sh imx8_775C_flash_64 -v imx8_775C_flash_64 -L 6 /data/arm/SAF775C_Arm_Ffs_nxp_pro_encrypted.bin 0 imx8_775C_flash_64 -R imx8_775C_flash_64 -E imx8_775C_flash_64 -F 0 /data/radio/dsp0/SAF775C_BBP_E7A0_nxp_pro_encrypted.bin 0 imx8_775C_flash_64 -F 1 /data/radio/dsp1/SAF775C_BBP_E7A1_nxp_pro_encrypted.bin 0 imx8_775C_flash_64 -F 2 /data/radio/dsp2/SAF775C_BBP_E7A2_nxp_pro_encrypted.bin 0 imx8_775C_flash_64 -F 3 /data/audio/dsp0/SAF775C_ABB_E7A0_nxp_pro_encrypted.bin 0 imx8_775C_flash_64 -F 4 /data/audio/dsp1/SAF775C_ABB_E7A1_nxp_pro_encrypted.bin 0 imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_dram0_offset0_nxp_pro_encrypted.bin 0 imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_dram0_offset15_nxp_pro_encrypted.bin 1 imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_dram1_offset16_nxp_pro_encrypted.bin 2 imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_dram1_offset31_nxp_pro_encrypted.bin 3 imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset80_nxp_pro_encrypted.bin 4 imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset95_nxp_pro_encrypted.bin 5 imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset110_nxp_pro_encrypted.bin 6 imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset125_nxp_pro_encrypted.bin 7 imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset140_nxp_pro_encrypted.bin 8 imx8_775C_flash_64 -F 6 /data/arm/SAF775C_ICC_ARM0_upper_nxp_pro_encrypted.bin 0 imx8_775C_flash_64 -F 6 /data/arm/SAF775C_ICC_ARM0_lower_nxp_pro_encrypted.bin 1 |
i.MX8QM SAF775C 固件升级 ,首先 确认 i.MX8 和 SAF775C 之间 I2C 或是 SPI 通信正常 ,接着对 D3DL.cpp 内容进行移植 ,然后在 Android 端编写应用程序 ,并打通 APP 和 Kernel 间的通信 ,以上完成后开始烧录固件 ,通过以上内容相信大家已经了解怎么通过 Android 端编写应用程序来对 SAF775C 固件升级
五. 参考文档
【1】NXP UM SAF775X Hifi Software User Manual.pdf Rev. 1.0 — 05 June 2017
【2】NXP SAF775X_UM_DIONEDL Dione Download Tool Rev. <0.1> — 14 June 2017
评论