新手也能搞定的PGL22G FPGA实战从零搭建一个能存图、能传图的片上系统第一次接触FPGA开发时看着板子上密密麻麻的引脚和复杂的开发环境我完全不知道从何下手。直到在导师的指导下完成了第一个图像采集存储项目才发现FPGA开发其实并没有想象中那么可怕。本文将带你用紫光同创PGL22G开发板从零开始构建一个完整的片上系统实现图像采集、TF卡存储和网络传输的全流程。即使你是刚接触FPGA的本科生或爱好者只要跟着步骤操作两天内就能做出可演示的作品。1. 开发环境搭建与基础验证1.1 硬件准备清单在开始前请确保你已准备好以下硬件紫光同创PGL22G开发板大赛指定型号配套的JTAG下载器8GB以上容量的TF卡建议Class10以上速度网线支持1000Mbps传输USB转TTL串口模块用于调试输出图像传感器模块推荐OV7670性价比较高注意不同批次的PGL22G开发板外设接口可能略有差异建议先查阅随板资料确认引脚定义。1.2 软件工具安装开发需要以下软件环境# Windows环境下推荐安装顺序 1. 安装Pango Design Suite紫光官方IDE 2. 安装Keil MDKARM开发环境 3. 安装串口调试工具如SecureCRT 4. 安装网络调试助手如TCP/UDP调试工具软件配置关键点Pango Design Suite需选择与板卡型号匹配的设备库Keil要安装Cortex-M1设备支持包设置环境变量时注意路径不要包含中文1.3 最小系统验证我们先构建一个能点亮LED的基础系统在Pango中新建工程选择PGL22G器件添加M1软核IP配置时钟为50MHz生成Keil工程模板编写GPIO测试代码#include gpio.h int main() { GPIO_Init(); while(1) { uint8_t sw_val GPIO_ReadInput(); GPIO_WriteOutput(sw_val); delay_ms(100); } }综合生成bit文件下载到FPGA用杜邦线连接开关和LED到指定GPIO观察是否随开关变化这个简单验证确保软核和基础外设工作正常为后续复杂功能打下基础。2. 构建带文件系统的片上系统2.1 软核外设配置在Pango中需要正确配置这些关键外设存储器接口分配Block RAM给软核SDIO控制器连接TF卡槽MAC控制器连接PHY芯片UART用于调试输出推荐配置参数外设地址范围中断号时钟频率GPIO0x400000001650MHzSDIO0x400100001725MHzMAC0x4002000018125MHzUART0x4003000019115200bps2.2 FatFS文件系统移植TF卡存储需要文件系统支持我们选用开源的FatFS下载FatFS源码R0.14b版本兼容性最好修改diskio.c实现SDIO底层驱动DSTATUS disk_initialize(BYTE pdrv) { SD_Init(); // 调用SDIO初始化函数 return RES_OK; }在Keil中添加文件系统组件右键工程→Manage→Run-Time Environment勾选File System→FatFS测试文件读写FIL fil; f_open(fil, test.txt, FA_WRITE | FA_CREATE_ALWAYS); f_printf(fil, PGL22G test %d\n, 123); f_close(fil);2.3 图像存储实现采集到的图像需要转换为BMP格式存储定义BMP文件头结构体写入图像数据时先填充头信息#pragma pack(1) typedef struct { uint16_t type; uint32_t size; uint32_t reserved; uint32_t offset; // ... 其他头字段 } BMPHeader;存储流程传感器获取一帧图像转换为RGB565格式生成唯一文件名如IMG_001.bmp写入TF卡并校验提示图像尺寸不宜过大320x240分辨率约占用150KB空间。3. 网络传输模块实现3.1 LWIP协议栈移植UDP传输需要网络协议栈支持下载LWIP 2.1.2稳定版源码修改arch/cc.h适配M1架构#define LWIP_PROVIDE_ERRNO 1 typedef uint32_t u32_t;配置lwipopts.h关键参数#define MEM_SIZE (1024*20) #define PBUF_POOL_SIZE 30 #define LWIP_UDP 13.2 UDP传输实现建立图像传输通道的步骤初始化MAC和PHYETH_Init(); while(ETH_GetLinkState() 0); // 等待网线连接创建UDP控制块并绑定端口struct udp_pcb *upcb udp_new(); udp_bind(upcb, IP_ADDR_ANY, 5000);实现发送函数void send_image(char *filename) { FIL fil; f_open(fil, filename, FA_READ); while(!f_eof(fil)) { UINT br; f_read(fil, buf, 1024, br); udp_sendto(upcb, pbuf, ipaddr, 6000); } f_close(fil); }3.3 上位机接收程序Python示例代码展示如何接收并显示图像import socket import cv2 sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((0.0.0.0, 6000)) data b while True: chunk, _ sock.recvfrom(1024) data chunk if len(chunk) 1024: # 假设最后包小于1024 img cv2.imdecode(np.frombuffer(data), 1) cv2.imshow(RX, img) data b4. 系统集成与性能优化4.1 资源占用分析完成基本功能后查看资源使用情况模块LUT使用BRAM使用时钟频率M1核523218%50MHzSDIO8742%25MHzMAC14215%125MHz图像处理21038%75MHz4.2 传输速度优化技巧通过以下方法提升系统性能双缓冲技术当一帧图像在传输时下一帧已经开始采集数据压缩在存储前用RLE算法压缩图像数据DMA传输配置SDIO和MAC使用DMA减少CPU负载时钟优化将图像处理模块时钟提升到100MHz优化前后对比指标优化前优化后采集帧率5fps15fps存储速度200KB/s800KB/s传输延迟500ms150ms4.3 常见问题排查遇到问题时可以按此流程检查TF卡无法识别检查电压是否为3.3V尝试重新格式化FAT32分配单元大小16KB网络连接失败用示波器检查PHY晶振是否起振确认开发板与电脑在同一网段图像显示异常检查传感器时钟极性配置确认BMP头信息填写正确记得在答辩演示前进行至少10次完整流程测试确保系统稳定。我在第一次参赛时就因为没充分测试现场出现了TF卡接触不良的尴尬情况。