做光 模块固件这行,选MCU这件事这几年越来越不省心。以前一颗Cortex-M0甚至一颗入门级8位机都能把SFP模块管起来,I2C从机、五路DDM采样、激光器偏置控制,翻来覆去就那么点活;现在光模块往400G、800G走,CMIS协议从4.0一路迭代到5.0,固件要管DSP联动、在线升级、多应用切换,老平台明显吃力。GigaDevice这次针对光通信推出的GD32E512和GD32E252系列MCU,光看命名就是冲着光模块这个应用场景来的。这篇文章我把这两颗料的规格、选型逻辑和实际固件开发中的关键点一次说清楚,准备做光模块或者想把现有设计迁到GD32 上的工程师,应该用得着。我尽量不堆参数表,重点讲“为什么这么设计”和“实际调试会踩什么坑”。
1. 光模块MCU到底在干什么:从SFF-8472到CMIS
1.1 一颗不起眼的MCU,凭什么成了模块标配
可插拔光模块内部通常有TOSA发射组件、ROSA接收组件、激光驱动芯片LDD、跨阻放大器TIA,高速方案里还有DSP或CDR负责信号恢复。MCU不参与光电信号的实时链路,它更像一个管家:主机通过I2C总线访问模块时,MCU负责响应协议、返回设备信息、上报实时状态;激光器需要稳定输出时,MCU配合LDD做功率控制;模块在交换机里运行久了温度升高,MCU还要根据温度变化做补偿。
早年不少低速模块根本不用MCU,只放一颗EEPROM存厂商信息,主机一上电读到一串静态的序列号就算完事。后来客户要求实时监控模块健康状态,SFF-8472标准把数字诊断监控DDM/DOM定成必选功能,模块里才开始大规模使用真正意义上的MCU。这颗MCU承担的活包括:
作为I2C从机,响应A0h和A2h两个地址的访问,管理协议寄存器。
采集温度、供电电压、激光器偏置电流、发射光功率、接收光功率五路模拟量。
处理出厂校准数据,把ADC原始值换算成带物理单位的光功率、温度值。
对比阈值寄存器,光功率异常或者电压越限时主动拉高告警引脚。
控制LDD的偏置或者调制电流,配合背光二极管实现自动功率控制APC。
这些任务里,I2C从机实时响应和DDM数据精度是核心。很多工程师第一次做光模块时容易轻敌,觉得写个EEPROM模拟器就行,真到主机一轮一轮扫描寄存器、要求几十个字节数据一路读出来不卡顿的时候,MCU的中断处理能力、I2C外设的成熟度、RAM够不够缓存页面数据,差距一下就出来了。
1.2 协议复杂度爆炸,MCU被迫“升级”
早期SFP模块的SFF-8472协议其实不算复杂,A0h地址256字节放静态识别信息,A2h地址256字节放监控数据、阈值和校准常量,很多老工程师对着数据手册几天就能把代码写完。等做到QSFP,SFF-8636引入了分页机制,CSR空间被拆成低页和多个高页,读数据前得先往某个寄存器写page号,逻辑复杂度上了一个台阶。
到了QSFP-DD和OSFP,Common Management Interface Specification(CMIS)协议成了主流。CMIS 4.0、5.0把模块状态机、应用选择、媒体类型配置、固件管理、低功耗模式全塞进了一套庞大的内存映射里。MCU不但要正确响应主机发来的读写请求,还要管理CDB命令(command data block),跑命令队列,处理模块状态迁移。这些逻辑加起来,Flash小于256KB、RAM只有十几KB的MCU会觉得非常吃力,尤其固件在线升级功能出现后,Bootloader、App、参数区几块代码一放,存储空间立刻紧张。
这就是GigaDevice推GD32E512和GD32E252这两档产品的原因:光模块市场并不是只有“越强越好”一种需求,SFP/SFP+这类成本敏感的模块需要一颗便宜、够用、低功耗的MCU,而400G/800G这类智能光模块需要一个性能充裕、外设齐全的管理核心。高低搭配,才覆盖得了整个产品线。
2. GD32E512和GD32E252规格拆解:为什么说它们是冲着光模块来的
2.1 从命名和产品线看定位差异
GD32E512从型号命名和GigaDevice现有产品线的习惯看,应该归到E5 高性能分支,走的是Cortex-M33内核路线,主频做到百MHz级别,Flash和SRAM给得比较足,适合做高速模块的“大脑”。GD32E252则更像是E230系列在光模块应用上的加强版,Cortex-M23内核,主频不需要太高,但低功耗和成本控制做得比较好,封装也偏小型化,适合塞进SFP模块那点有限的空间里。下面这张表是我根据公开资料和产品线规律整理的对比,具体数值还是要以官方数据手册为准:
选型时没必要纠结“E512肯定比E252好”。高速模块控制板需要跟DSP频繁交换数据、可能还要跑以太网管理口,E512的外设余量和算力价值明显;但一颗只需要管A0h/A2h寄存器、做五路DDM采样的SFP模块,用E512属于浪费成本和功耗。真正会做光模块的工程师,手里通常两种方案都会备着,硬件平台尽量兼容,固件用宏隔离差异,这样客户要低成本版本还是高性能版本都能快速响应。
2.2 外设设计躲不开的细节
光模块MCU选型,外设重点不在多,而在“合不合适”。我重点看几个点。
第一是I2C从机能力。光模块MCU的I2C绝大多数时间工作在从机模式,主机是交换机或光模块测试设备的I2C主控。这里要关注MCU能不能配置多个从机地址,能不能在硬件层响应A0h/A2h两个地址的中断。GD32系列的I2C做从机时,有独立的地址匹配中断、收发缓冲中断和错误中断,处理得好可以做到一个字节进一个中断,响应很及时。
第二是ADC精度和采样通道。五路遥测信号里有电压、有光功率、有温度,动态范围还不太一样。GD32E512和GD32E252集成的都是12位ADC,多通道做顺序扫描,配合DMA搬运,CPU开销很低。实际项目中关键是校准,光功率和温度都有非线性,需要做两点甚至多点校准,校准系数要存在Flash里,这就要看MCU有没有好用的数据Flash区。
第三是DAC或PWM输出。APC控制里MCU要给LDD一个模拟控制电压,有DAC的产品可以直接输出干净电平,没有DAC就用PWM加RC滤波。GD32E512和GD32E252按系列配置,通常都保留了DAC能力,这对光模块设计来说省了一颗外部DAC芯片,BOM成本是有意义的。
第四是UART和SPI。UART用来跟DSP通信或者做调试日志输出,SPI可以用来连外部EEPROM或者高精度传感器。光模块PCB空间寸土寸金,外设能省则省,但关键接口不能缺。
3. 实战:从零搭建GD32E512光模块固件框架
3.1 开发环境与工程模板
GD32的开发工具链已经比较成熟了。在Keil MDK里需要安装对应的Device Family Pack,也就是DFP,这样工程里才能正确识别GD32E512的器件型号和启动文件;IAR这边则装官方Board Support Package。如果不喜欢折腾第三方IDE,可以直接用GigaDevice自家的GD32 Embedded Builder,它基于Eclipse,官方把例程、库函数、烧录工具都整合好了,新建工程时选型号就能生成一个可编译的模板。
很多从STM32 转过来的工程师第一反应是直接把原有工程改改芯片型号。这个思路要小心:GD32虽然和STM32的引脚兼容、库函数风格接近,但GPIO复用映射、时钟树配置、外设寄存器位定义都有差异。我建议第一次做GD32E512时先用官方模板建工程,把默认时钟初始化流程跑通,再把你需要的I2C、ADC、DMA外设一个个加进去,别一上来就大改。
时钟配置是第一步,也是最容易出错的地方。GD32E5xx的RCU模块里,APB1和APB2预分频值、定时器时钟倍频位都会影响外设工作频率。工程模板一般默认用外部晶振通过PLL倍频到系统主频,但这个主频和APB分频是不是符合你的设计预期,最好在初始化完成后调用 rcu_clock_freq_get() 实际读一下,别只看注释。
3.2 I2C从机与MSA寄存器访问
光模块固件的核心是I2C从机协议。以GD32E512为例,I2C从机初始化其实不复杂,关键是中断处理函数要短、要快。我摘了一段典型的初始化代码:
void i2c_slave_init(void)
{
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_I2C0);
gpio_af_set(GPIOB, GPIO_AF_4, GPIO_PIN_6 | GPIO_PIN_7);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6 | GPIO_PIN_7);
gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
i2c_clock_config(I2C0, 100000, I2C_DTCY_2);
i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BIT, 0x50);
i2c_enable(I2C0);
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
i2c_interrupt_enable(I2C0, I2C_INT_ERR | I2C_INT_BUF | I2C_INT_EV);
}
void I2C0_IRQHandler(void)
{
if(i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_RBNE)) {
uint8_t val = i2c_data_receive(I2C0);
// 根据当前接收状态机处理val,写入对应地址
}
if(i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_TBE)) {
i2c_data_transmit(I2C0, msa_read_buffer[tx_index++]);
}
}
注意这里的从机地址我填的是0x50,也就是A0h地址去掉读写位后的7位地址。如果你还需要同时响应A2h,GD32硬件上支持配置自己的地址掩码,或者通过地址匹配中断判断当前命中哪个地址。实际工程里我建议不要在一次中断里做太多协议解析,先把收到的字节按顺序存进环形缓冲区,等一帧完整的I2C事务结束后,再调用状态机更新MSA寄存器内容。中断里处理逻辑太长,数据一多就会拖慢响应,严重的会把I2C时钟拉低,主机侧看到的就是“总线卡死”。
还有一个从STM32移植过来容易踩的坑:GD32的I2C中断标志名和STM32不完全一样,直接用STM32的固件库函数名替换是编译不过的。比如接收缓冲区非空标志,GD32里是 I2C_INT_FLAG_RBNE ,中断使能也要区分 I2C_INT_ERR 、 I2C_INT_BUF 、 I2C_INT_EV ,跟老系列不完全一样。保持“看数据手册写代码”的习惯,比靠肌肉** 快得多。
3.3 ADC+DMA+TIMER联合采样,DDM数据才稳
DDM五路监测数据不能靠主循环里轮询读取,原因很简单:主循环一旦被I2C中断频繁打断,采样间隔就会不均匀,温度、光功率曲线看起来就一抖一抖的。我习惯用定时器触发ADC启动,ADC转换完成后由DMA把结果搬进内存缓冲区,形成一条完整的数据通路。
大概步骤是:先配置一个定时器,让它周期产生触发事件;再把ADC的外部触发源指向这个定时器,使能ADC扫描模式;最后配置DMA循环搬运ADC转换结果。下面是DMA初始化部分的示意代码:
#define ADC_CH_NUM 5
uint16_t adc_buf[ADC_CH_NUM * 8];
void adc_dma_init(void)
{
dma_single_data_parameter_struct dma_param;
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(DMA0, DMA_CH0);
dma_param.periph_addr = (uint32_t)(&ADC_RDATA);
dma_param.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_param.memory0_addr = (uint32_t)adc_buf;
dma_param.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_param.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;
dma_param.direction = DMA_PERIPH_TO_MEMORY;
dma_param.number = ADC_CH_NUM * 8;
dma_param.circular_mode = DMA_CIRCULAR_MODE_ENABLE;
dma_single_data_mode_init(DMA0, DMA_CH0, &dma_param);
dma_circulation_enable(DMA0, DMA_CH0);
dma_channel_enable(DMA0, DMA_CH0);
}
DMA配置成循环模式后,ADC会一直采样、一直搬运,固件只需要周期性从 adc_buf 里取一段数据做平均就够了。我一般取8次或16次平均值,作为一帧DDM数据的原始值,这样抗干扰能力比单次采样好很多。别小看这个细节,光模块在交换机里运行时,电源纹波和串扰会让ADC值低频跳变,不做滤波,光功率读数就容易出现0.1dB以上的抖动,上线后客户拿标准模块一对比就露馅了。
ADC还牵涉到校准。GD32内部有一个参考电压通道,可以通过测量内部参考电压来反推实际VDD,再校正ADC满量程。这个方法能解决芯片之间基准电压分散的问题,但对光模块这种对绝对精度有要求的场景,还必须在产线上对每一路光功率做标准光源标定,把增益和偏移系数写进Flash。软件里读取DDM时,先查校准系数,再代入线性公式换算成光功率值。
3.4 RTOS还是裸机,取决于你的软件复杂度
光模块固件的业务量其实可控,很多老工程师坚持裸机前后台架构:一个超级循环
————————————————
版权声明:本文为CSDN博主「thongzzz」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42709044/article/details/164080306
|