最近在学习USB通信相关的知识,联想到Ai8051有usb,也有40M主频,
能不能模拟网上24M那种赛普拉斯单片机的逻辑分析仪呢。
试了一下还真行,虽然性能差了不是一星半点,但是真能跑。
先放张能跑的图,上位机使用的是开源的pulseView
通过抓包和查看fx2lafw固件的源码可以知道,sigrok使用libusb,通过厂商描述符与下位机通信。
设备描述符,配置描述符和字符串照抄源码即可,由于成品逻辑分析仪是高速设备,所以对应的端点描述符最大包要从512改为64
setup相关配置照抄USBCDC官方例程即可。
需要添加的是厂商描述符的处理,
固件中相关源码
- BOOL handle_vendorcommand(BYTE cmd)
-
- {
-
- /* Protocol implementation */
-
- switch (cmd) {
-
- case CMD_START:
-
- /* Tell hardware we are ready to receive data. */
-
- vendor_command = cmd;
-
- EP0BCL = 0;
-
- SYNCDELAY();
-
- return TRUE;
-
- case CMD_GET_FW_VERSION:
-
- send_fw_version();
-
- return TRUE;
-
- case CMD_GET_REVID_VERSION:
-
- send_revid_version();
-
- return TRUE;
-
- }
-
-
-
- return FALSE;
-
- }
复制代码
这里cmd是一个结构体
- struct cmd_start_acquisition {
-
- uint8_t flags;
-
- uint8_t sample_delay_h;
-
- uint8_t sample_delay_l;
-
- };
复制代码
flags是为30M主频还是48M主频,sample_delay是主频/采样率-1,我这里固定主频为40M,将他的采样率转化为40M的定时器计数
- void cmd_start_over()
-
- {
-
- //还原采样率
-
- u32 u32Samplerate;
-
- //接收采样率 单位Hz 定时器设置值
-
- u16 u16Delay;
-
- //未添加错误处理
-
-
- u16Delay=cmdStartInfo.sample_delay_h;
-
- u16Delay<<=8;
-
- u16Delay+=cmdStartInfo.sample_delay_l;
-
- u16Delay++;
-
- //1<<6
-
- if(cmdStartInfo.flags==0x40)//48M基准
-
- {
-
- //delay = SR_MHZ(48) / samplerate - 1;
-
- u32Samplerate=48000000UL/u16Delay;
-
-
- }
-
- else//30M基准 0<<6
-
- {
-
- u32Samplerate=30000000UL/u16Delay;
-
- }
-
-
- //初始化缓存
-
- Buffer_Init();
-
- //启动定时器
-
- Timer0_Start(u32Samplerate);
-
- }
-
-
-
- //启动定时器
-
- void Timer0_Start(uint32_t xHz) //可配置频率@40.000MHz
-
- {
-
- uint32_t reload;
-
-
-
- AUXR |= 0x80; //定时器时钟1T模式
-
- TMOD &= 0xF0; //设置定时器模式
-
-
-
- // 计算重载值
-
- // 系统时钟频率为40MHz,1T模式下每个时钟周期为1/40MHz = 0.025us
-
- // 定时周期 = 1/xHz 秒 = (1/xHz)*1000000 us
-
- // 需要的时钟周期数 = (定时周期)/0.025 = (1000000/xHz)/0.025 = 40000000/xHz
-
- // 由于定时器是16位向上计数,重载值 = 65536 - (40000000/xHz)
-
- reload = 65536UL - (40000000UL / xHz);
-
-
-
- TL0 = reload; //设置定时初始值低字节
-
- TH0 = reload >> 8; //设置定时初始值高字节
-
-
-
- TF0 = 0; //清除TF0标志
-
- TR0 = 1; //定时器0开始计时
-
- ET0 = 1; //使能定时器0中断
-
- }
复制代码
定时器启动后就开始进行采样,采样数据8通道占1个字节,0通道是bit0,7通道是bit7。
Ai8051有一个32K大小的xdata内存,尽可能分一个大的环形缓冲区,一个指针读,一个指针写,到达末尾就返回初始,这样可以最大限度利用缓冲区。
- //定时器中断,填入数据
-
- void Timer0_Isr(void) interrupt 1
-
- {
-
- BYTE xdata *nextPtr = wPtr + 1;
-
- if (nextPtr >= bufferEnd) { // 指针回绕
-
- nextPtr = RxBuffer;
-
- }
-
- if (nextPtr == rPtr) { // 缓冲区满
-
- //memOver=1;
-
- Timer0_Stop();
-
- }
-
- *wPtr = P1;
-
- u16Remain--;//剩余空间-1
-
- u16Available++;
-
- wPtr = nextPtr;//指针+1
-
- }
-
- 在主循环中,只需要不停的发送这些数据就可以了。
-
- //判断usb空闲
-
- if(!UsbInBusy)
-
- {
-
- //已用空间达到64
-
- if(u16Available>=64)
-
- {
-
- //发送数据
-
- EUSB =0;
-
- UsbInBusy=1;
-
- usb_write_reg(INDEX, 2);
-
- for(i=0;i<64;i++)
-
- {
-
- usb_write_reg(FIFO2, *rPtr); //发送接收到的数据
-
- rPtr++;
-
- if (rPtr >= bufferEnd) { // 指针回绕
-
- rPtr = RxBuffer;
-
- }
-
- }
-
- u16Remain+=64;//剩余空间+64
-
- u16Available-=64;
-
- usb_write_reg(INCSR1, INIPRDY);
-
- while (usb_read_reg(INCSR1) & INIPRDY);
-
- usb_write_reg(INCSR1, INIPRDY);
-
- EUSB = 1;
-
- }
-
- }
复制代码
代码比较简单,基本上就是拿官方例程改了改,不过现在的版本还有很大问题
可能是有丢包或者其他情况,500K和1M采的数据完全不可用,降到250K才可用,并且受到通信速度和算法垃圾的影响,1M采样率只能采30ms,500K采样率可采120ms,250K及以下可以长时间采集
就这样吧,抛砖引玉,等一个感兴趣的大佬来优化了
最后,使用方法参考网上卖的20左右的逻辑分析仪,源码见附件
25.7.13
调整了定时器0的中断优先级
在P01增加了一个100K占空比50%的pwm测试波形
修复了P35没有时钟输出的bug
目前来说500K虽然只能112ms,还比较稳定,可用
fx2lafw模拟.7z
(78.75 KB, 下载次数: 3)
参考文档、视频
Ai8051产品规格书
https://www.stcaimcu.com/data/download/Datasheet/AI8051U.pdf
USB拓展库及使用示例 | 本贴 咨询 USB,基本帮您把USB程序开发完成
https://www.stcaimcu.com/thread-16791-1-1.html
STCAI 官方usb课程
视频教学:【USB 原理及实战,16课时】,大学标准课程,有配套书籍 - USB:USB-CDC虚拟串口/就是串口,一箭双雕之USB转双串口,[鼠标+键盘]的HID复合设备 国芯技术交流网站 - AI32位8051交流社区
上位机通信源码
https://github.com/sigrokproject ... rc/hardware/fx2lafw
下位机固件源码
https://github.com/sigrokproject ... fx2lafw/tree/master
|