苏紫方璇 发表于 2025-7-12 23:14:07

使用Ai8051模拟fx2lafw设备制作简易逻辑分析仪(抛砖引玉)

最近在学习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,还比较稳定,可用



参考文档、视频
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












苏紫方璇 发表于 2025-7-12 23:51:40

忘了说了p10-p17是0-7通道,p35是定时器输出端

晓飛飛 发表于 2025-7-13 00:55:39

精神可嘉,值得鼓励!
虽然cy7c68013A是51核,而且是20年前的老产品,但它的USB接口是2.0高速接口,支持480Mbps,USB单元设置了一个GPIF接口,通过外部EEPROM配置为IO映射GPIPF的方式通信,完全不需要单片机核的介入,类似纯物理搬运IO状态,所以能到24MHz采样速率。
而这种模拟的方式受限因素就多了,软件参与过多,而且缓冲机制也会造成一定的数据延迟,做USB接口的高速IO板卡还算是能实用,但做逻辑分析仪的话瓶颈就放在那,优化空间也不是很大。

苏紫方璇 发表于 2025-7-13 11:10:30

晓飛飛 发表于 2025-7-13 00:55
精神可嘉,值得鼓励!
虽然cy7c68013A是51核,而且是20年前的老产品,但它的USB接口是2.0高速接口,支持480 ...
是的,看了固件的源码,那个芯片io单独编程直连USB-DMA发送,这个主要还是学习usb的作品,本来想着能识别出来就完事了,没想到还真能跑

苏紫方璇 发表于 2025-7-15 11:41:44

量了一下逻辑分析仪的正规产品,引脚是有一个100k左右的上拉的,所以P1引脚应设为准双向口,对外输出1。这样抗干扰能力有所提升,再也不会相邻悬空口也出现采集的干扰波形了
页: [1]
查看完整版本: 使用Ai8051模拟fx2lafw设备制作简易逻辑分析仪(抛砖引玉)