| 最近在学习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, 下载次数: 32) 
 
 参考文档、视频
 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
 
 
 
 
 
 
 
 
 
 
 
 
 
 |