你的位置:首页 > 操作系统

[操作系统]基于TQ2440的SPI驱动学习(OLED)


平台简介

开发板:TQ2440 (NandFlash:256M  内存:64M)

u-boot版本:u-boot-2015.04

内核版本:Linux-3.14

作者:彭东林

邮箱:pengdonglin137@163.com

摘要

这篇博客的目的是简要分析两种spi驱动的实现,一种是利用Samsung的S3C2440自带的硬件SPI控制器,另一种是利用Linux内核已经写好的用GPIO模拟SPI时序,实现一个软件SPI控制器。操作的外设是韦东山的SPI视频教程中提供的OLED模块,同时分享一下在使用逻辑分析仪Saleae16调试SPI时遇到的问题。

相关的内核代码已经上传:git@code.csdn.net:pengdonglin137/linux-3-14-y.git

可以看看代码提交记录。

正文

SPI驱动实现之硬件控制器

一、驱动框架

 

image

 

二、代码

SPI硬件控制器

这里采用的是platform架构,分为device和driver两个部分。

1、platform_device

文件:arch/arm/plat-samsung/devs.c

  1: static struct resource s3c_spi0_resource[] = {
  2:   [0] = DEFINE_RES_MEM(S3C24XX_PA_SPI, SZ_32),
  3:   [1] = DEFINE_RES_IRQ(IRQ_SPI0),
  4: };
  5:  
  6: static void s3c24xx_spi_set_cs(struct s3c2410_spi_info *spi, int cs, int pol)
  7: {
  8:   gpio_set_value(cs, pol);
  9: }
 10:  
 11: static struct s3c2410_spi_info s3c_spi_info[] = {
 12:   {
 13:     .num_cs = S3C_GPIO_END,
 14:     .bus_num = 0,
 15:     .set_cs = s3c24xx_spi_set_cs,
 16:   }
 17: };
 18:  
 19: struct platform_device s3c_device_spi0 = {
 20:   .name    = "s3c2410-spi",
 21:   .id    = 0,
 22:   .num_resources  = ARRAY_SIZE(s3c_spi0_resource),
 23:   .resource  = s3c_spi0_resource,
 24:   .dev    = {
 25:     .dma_mask    = &samsung_device_dma_mask,
 26:     .coherent_dma_mask  = DMA_BIT_MASK(32),
 27:     .platform_data    = (void *)s3c_spi_info,
 28:   }
 29: };


第15行是片选函数,它的第二个参数cs来自spi从设备的板级信息,表示这个从设备的片选引脚;

第14行表示spi控制器的编号是0,将来在spi从设备的板级信息中有体现,意思是将来这个spi从设备挂载在编号为0的spi总线下面;

第27行,在linux原生的代码中没有实现platform_data,在调用probe函数的时候会报错;

2、platform_driver

文件:drivers/spi/spi-s3c24xx.c

  1: MODULE_ALIAS("platform:s3c2410-spi");
  2: static struct platform_driver s3c24xx_spi_driver = {
  3:   .probe    = s3c24xx_spi_probe,
  4:   .remove    = s3c24xx_spi_remove,
  5:   .driver    = {
  6:     .name  = "s3c2410-spi",
  7:     .owner  = THIS_MODULE,
  8:     .pm  = S3C24XX_SPI_PMOPS,
  9:   },
 10: };
 11: module_platform_driver(s3c24xx_spi_driver);
 12:  


 

OLED 板级信息

这里调用了spi子系统提供的函数接口。

1、板级信息

文件:arch/arm/mach-s3c24xx/mach-tq2440.c

  1: /* SPI OLED */
  2: static struct spi_board_info tq2440_spi_board_info[] __initdata = {
  3:   {
  4:     .modalias  = "oled",
  5:     .max_speed_hz  = 10000000,
  6:     .bus_num  = 0,
  7:     .mode    = SPI_MODE_0,
  8:     .chip_select  = S3C2410_GPG(1),
  9:     .platform_data  = (const void *)S3C2410_GPF(3),
 10:   },
 11: };
 12:  
 13: static struct platform_device *tq2440_devices[] __initdata = {
 14:   ......
 15:   &s3c_device_spi0,
 16: };
 17:  
 18: static void __init tq2440_machine_init(void)
 19: {
 20: ......
 21:   spi_register_board_info(tq2440_spi_board_info, ARRAY_SIZE(tq2440_spi_board_info));
 22: ......
 23: }
 24:  
 25: MACHINE_START(TQ2440, "TQ2440")
 26: ......
 27:   .init_machine  = tq2440_machine_init,
 28: ......
 29: MACHINE_END


第4行,将来会跟驱动中的name进行匹配;

第5行,表示通信速率,这里设置的是10MHz;

第6行,表示使用的spi总线的编号是0;

第7行,表示使用的spi模式是0,这里要根据oled的芯片手册(SSD1306-Revision 1.1 (Charge Pump).pdf)

第8行,oled使用的片选引脚;

第9行,用于区分命令和数据模式的GPIO资源,这个会在驱动中解析;

第21行,注册spi从设备板级信息;

2、oled驱动

文件:drivers/spi/oled/spi_oled_drv.c

  1: #include <linux/init.h>
  2: #include <linux/fs.h>
  3: #include <linux/slab.h>
  4: #include <linux/module.h>
  5: #include <linux/kernel.h>
  6: #include <linux/device.h>
  7: #include <sound/core.h>
  8: #include <linux/spi/spi.h>
  9: #include <asm/uaccess.h>
 10:  
 11: #include <mach/hardware.h>
 12: #include <mach/regs-gpio.h>
 13:  
 14: #include <linux/gpio.h>
 15: #include <plat/gpio-cfg.h>
 16:  
 17: /* 构造注册 spi_driver */
 18:  
 19: static int major;
 20: static struct class *class;
 21:  
 22: static int spi_oled_dc_pin;
 23: static struct spi_device *spi_oled_dev;
 24: static unsigned char *ker_buf;
 25:  
 26: static void OLED_Set_DC(char val)
 27: {
 28:   gpio_set_value(spi_oled_dc_pin, val);
 29: }
 30:  
 31: static void OLEDWriteCmd(unsigned char cmd)
 32: {
 33:   OLED_Set_DC(0); /* command */
 34:   spi_write(spi_oled_dev, &cmd, 1);
 35:   OLED_Set_DC(1); /* */
 36: }
 37:  
 38: static void OLEDWriteDat(unsigned char dat)
 39: {
 40:   OLED_Set_DC(1); /* data */
 41:   spi_write(spi_oled_dev, &dat, 1);
 42:   OLED_Set_DC(1); /* */
 43: }
 44:  
 45: static void OLEDSetPageAddrMode(void)
 46: {
 47:   OLEDWriteCmd(0x20);
 48:   OLEDWriteCmd(0x02);
 49: }
 50:  
 51: static void OLEDSetPos(int page, int col)
 52: {
 53:   OLEDWriteCmd(0xB0 + page); /* page address */
 54:  
 55:   OLEDWriteCmd(col & 0xf);  /* Lower Column Start Address */
 56:   OLEDWriteCmd(0x10 + (col >> 4));  /* Lower Higher Start Address */
 57: }
 58:  
 59:  
 60: static void OLEDClear(void)
 61: {
 62:   int page, i;
 63:   for (page = 0; page < 8; page ++)
 64:   {
 65:     OLEDSetPos(page, 0);
 66:     for (i = 0; i < 128; i++)
 67:       OLEDWriteDat(0);
 68:   }
 69: }
 70:  
 71: void OLEDClearPage(int page)
 72: {
 73:   int i;
 74:   OLEDSetPos(page, 0);
 75:   for (i = 0; i < 128; i++)
 76:     OLEDWriteDat(0);  
 77: }
 78:  
 79: void OLEDInit(void)
 80: {
 81:   /* 向OLED发命令以初始化 */
 82:   OLEDWriteCmd(0xAE); /*display off*/ 
 83:   OLEDWriteCmd(0x00); /*set lower column address*/ 
 84:   OLEDWriteCmd(0x10); /*set higher column address*/ 
 85:   OLEDWriteCmd(0x40); /*set display start line*/ 
 86:   OLEDWriteCmd(0xB0); /*set page address*/ 
 87:   OLEDWriteCmd(0x81); /*contract control*/ 
 88:   OLEDWriteCmd(0x66); /*128*/ 
 89:   OLEDWriteCmd(0xA1); /*set segment remap*/ 
 90:   OLEDWriteCmd(0xA6); /*normal / reverse*/ 
 91:   OLEDWriteCmd(0xA8); /*multiplex ratio*/ 
 92:   OLEDWriteCmd(0x3F); /*duty = 1/64*/ 
 93:   OLEDWriteCmd(0xC8); /*Com scan direction*/ 
 94:   OLEDWriteCmd(0xD3); /*set display offset*/ 
 95:   OLEDWriteCmd(0x00); 
 96:   OLEDWriteCmd(0xD5); /*set osc division*/ 
 97:   OLEDWriteCmd(0x80); 
 98:   OLEDWriteCmd(0xD9); /*set pre-charge period*/ 
 99:   OLEDWriteCmd(0x1f); 
 100:   OLEDWriteCmd(0xDA); /*set COM pins*/ 
 101:   OLEDWriteCmd(0x12); 
 102:   OLEDWriteCmd(0xdb); /*set vcomh*/ 
 103:   OLEDWriteCmd(0x30); 
 104:   OLEDWriteCmd(0x8d); /*set charge pump enable*/ 
 105:   OLEDWriteCmd(0x14); 
 106:  
 107:   OLEDSetPageAddrMode();
 108:  
 109:   OLEDClear();
 110:  
 111:   OLEDWriteCmd(0xAF); /*display ON*/  
 112: }
 113:  
 114:  
 115: #define OLED_CMD_INIT    0x100001
 116: #define OLED_CMD_CLEAR_ALL 0x100002
 117: #define OLED_CMD_CLEAR_PAGE 0x100003
 118: #define OLED_CMD_SET_POS  0x100004
 119:  
 120: static long oled_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 121: {
 122:   int page;
 123:   int col;
 124:  
 125:   switch (cmd)
 126:   {
 127:   case OLED_CMD_INIT:
 128:     {
 129:       OLEDInit();
 130:       break;
 131:     }
 132:   case OLED_CMD_CLEAR_ALL:
 133:     {
 134:       OLEDClear();
 135:       break;
 136:     }
 137:   case OLED_CMD_CLEAR_PAGE:
 138:     {
 139:       page = arg;
 140:       OLEDClearPage(page);
 141:       break;
 142:     }
 143:   case OLED_CMD_SET_POS:
 144:     {
 145:       page = arg & 0xff;
 146:       col = (arg >> 8) & 0xff;
 147:       OLEDSetPos(page, col);
 148:       break;
 149:     }
 150:   }
 151:   return 0;
 152: }
 153:  
 154: static ssize_t oled_write(struct file *file,
 155:   const char __user *buf,
 156:   size_t count, loff_t *ppos)
 157: {
 158:   int ret;
 159:  
 160:   if (count > 4096)
 161:     return -EINVAL;
 162:   ret = copy_from_user(ker_buf, buf, count);
 163:   OLED_Set_DC(1); /* data */
 164:   spi_write(spi_oled_dev, ker_buf, count);
 165:   return 0;
 166: }
 167:  
 168:  
 169: static struct file_operations oled_ops = {
 170:   .owner      = THIS_MODULE,
 171:   .unlocked_ioctl  = oled_ioctl,
 172:   .write      = oled_write,
 173: };
 174:  
 175: static int spi_oled_probe(struct spi_device *spi)
 176: {
 177:   int ret;
 178:  
 179:   spi_oled_dev = spi;
 180:   spi_oled_dc_pin = (int)dev_get_platdata(&spi->dev);
 181:  
 182:   ret = devm_gpio_request(&spi->dev, spi_oled_dc_pin, "OLED_DC");
 183:   if (ret < 0)
 184:     return ret;
 185:   gpio_direction_output(spi_oled_dc_pin, 0);
 186:  
 187: #ifndef CONFIG_TQ2440_USE_SPI_GPIO
 188:   ret = devm_gpio_request(&spi->dev, spi->chip_select, "OLED_CHIP_SELECT");
 189:   if (ret < 0)
 190:     return ret;
 191:   gpio_direction_output(spi->chip_select, 1);
 192: #endif
 193:  
 194:   ker_buf = kmalloc(4096, GFP_KERNEL);
 195:  
 196:   /* 注册一个 file_operations */
 197:   major = register_chrdev(0, "oled", &oled_ops);
 198:  
 199:   class = class_create(THIS_MODULE, "oled");
 200:  
 201:   /* 为了让mdev根据这些信息来创建设备节点 */
 202:   device_create(class, NULL, MKDEV(major, 0), NULL, "oled"); /* /dev/oled */
 203:  
 204:   return 0;
 205: }
 206:  
 207: static int spi_oled_remove(struct spi_device *spi)
 208: {
 209:   device_destroy(class, MKDEV(major, 0));
 210:   class_destroy(class);
 211:   unregister_chrdev(major, "oled");
 212:  
 213:   kfree(ker_buf);
 214:  
 215:   return 0;
 216: }
 217:  
 218: static struct spi_driver spi_oled_drv = {
 219:   .driver = {
 220:     .name  = "oled",
 221:     .owner  = THIS_MODULE,
 222:   },
 223:   .probe    = spi_oled_probe,
 224:   .remove    = spi_oled_remove,
 225: };
 226:  
 227: static int spi_oled_init(void)
 228: {
 229:   return spi_register_driver(&spi_oled_drv);
 230: }
 231:  
 232: static void spi_oled_exit(void)
 233: {
 234:   spi_unregister_driver(&spi_oled_drv);
 235: }
 236:  
 237: module_init(spi_oled_init);
 238: module_exit(spi_oled_exit);
 239: MODULE_DESCRIPTION("OLED SPI Driver");
 240: MODULE_AUTHOR("weidongshan@qq.com,www.100ask.net");
 241: MODULE_LICENSE("GPL");


第187行,如果使用的是gpio模拟的spi的话,这个宏CONFIG_TQ2440_USE_SPI_GPIO会配置,这里我们使用的不是gpio模拟的,所以这个宏没有配置;

第182行,申请gpio,这里使用的函数是devm_gpio_request,它的好处是你不用再考虑gpio资源的释放了,系统会自动帮助你完成,类似的还有devm_kmalloc;

内核配置

System Type  --->

    SAMSUNG S3C24XX SoCs Support  --->

       [ ]   TQ2440 use spi gpio to communicate with peripherals

Device Drivers  --->

    [*] SPI support  --->

        <*>   Samsung S3C24XX series SPI

        <*>   Support TQ2440 OLED (from 100ask.com)

应用

1、oled_test.c
  1: #include <stdlib.h>
  2: #include <stdio.h>
  3: #include <string.h>
  4: #include <sys/types.h>
  5: #include <sys/stat.h>
  6: #include <fcntl.h>
  7: #include <unistd.h>
  8: #include <sys/ioctl.h>
  9:  
 10: #include "oledfont.h"
 11:  
 12: /* oled_test init
 13:  * oled_test clear
 14:  * oled_test clear <page>
 15:  * oled_test <page> <col> <string>
 16:  */
 17:  
 18: #define OLED_CMD_INIT    0x100001
 19: #define OLED_CMD_CLEAR_ALL 0x100002
 20: #define OLED_CMD_CLEAR_PAGE 0x100003
 21: #define OLED_CMD_SET_POS  0x100004
 22:  
 23:  
 24:  
 25: /* page: 0-7
 26:  * col : 0-127
 27:  * 字符: 8x16象素
 28:  */
 29: void OLEDPutChar(int fd, int page, int col, char c)
 30: {
 31:   int i = 0;
 32:   /* 得到字模 */
 33:   const unsigned char *dots = oled_asc2_8x16[c - ' '];
 34:  
 35:   /* 发给OLED */
 36:   //OLEDSetPos(page, col);
 37:   //ioctl(fd, OLED_CMD_CLEAR_PAGE, page);
 38:   ioctl(fd, OLED_CMD_SET_POS, page | (col << 8));
 39:   /* 发出8字节数据 */
 40:   //for (i = 0; i < 8; i++)
 41:   //  OLEDWriteDat(dots[i]);
 42:   write(fd, &dots[0], 8);
 43:  
 44:   //OLEDSetPos(page+1, col);
 45:   //ioctl(fd, OLED_CMD_CLEAR_PAGE, page+1);
 46:   ioctl(fd, OLED_CMD_SET_POS, (page+1) | (col << 8));
 47:   /* 发出8字节数据 */
 48:   //for (i = 0; i < 8; i++)
 49:   //  OLEDWriteDat(dots[i+8]);
 50:   write(fd, &dots[8], 8);
 51: }
 52:  
 53:  
 54:  
 55: /* page: 0-7
 56:  * col : 0-127
 57:  * 字符: 8x16象素
 58:  */
 59: void OLEDPrint(int fd, int page, int col, char *str)
 60: {
 61:   int i = 0;
 62:  
 63:   ioctl(fd, OLED_CMD_CLEAR_PAGE, page);
 64:   ioctl(fd, OLED_CMD_CLEAR_PAGE, page+1);
 65:   while (str[i])
 66:   {
 67:     OLEDPutChar(fd, page, col, str[i]);
 68:     col += 8;
 69:     if (col > 127)
 70:     {
 71:       col = 0;
 72:       page += 2;
 73:       ioctl(fd, OLED_CMD_CLEAR_PAGE, page);
 74:       ioctl(fd, OLED_CMD_CLEAR_PAGE, page+1);
 75:     }
 76:     i++;
 77:   }
 78: }
 79:  
 80:  
 81: void print_usage(char *cmd)
 82: {
 83:   printf("Usage:\n");
 84:   printf("%s init\n", cmd);
 85:   printf("%s clear\n", cmd);
 86:   printf("%s clear <page>\n", cmd);
 87:   printf("%s <page> <col> <string>\n", cmd);
 88:   printf("eg:\n");
 89:   printf("%s 2 0 100ask.taobao.com\n", cmd);
 90:   printf("page is 0,1,...,7\n");
 91:   printf("col is 0,1,...,127\n");
 92: }
 93:  
 94: int main(int argc, char **argv)
 95: {
 96:   int do_init = 0;
 97:   int do_clear = 0;
 98:   int do_show = 0;
 99:   int page = -1;
 100:   int col;
 101:  
 102:   int fd;
 103:  
 104:   if (argc == 2 && !strcmp(argv[1], "init"))
 105:     do_init = 1;
 106:   if ((argc == 2) && !strcmp(argv[1], "clear"))
 107:   {
 108:     do_clear = 1;
 109:   }
 110:   if ((argc == 3) && !strcmp(argv[1], "clear"))
 111:   {
 112:     do_clear = 1;
 113:     page = strtoul(argv[2], NULL, 0);
 114:   }
 115:   if (argc == 4)
 116:   {
 117:     do_show = 1;
 118:     page = strtoul(argv[1], NULL, 0);
 119:     col = strtoul(argv[2], NULL, 0);
 120:   }
 121:  
 122:   if (!do_init && !do_clear && !do_show)
 123:   {
 124:     print_usage(argv[0]);
 125:     return -1;
 126:   }
 127:  
 128:   fd = open("/dev/oled", O_RDWR);
 129:   if (fd < 0)
 130:   {
 131:     printf("can't open /dev/oled\n");
 132:     return -1;
 133:   }
 134:  
 135:   if (do_init)
 136:     ioctl(fd, OLED_CMD_INIT);
 137:   else if (do_clear)
 138:   {
 139:     if (page == -1)
 140:       ioctl(fd, OLED_CMD_CLEAR_ALL);
 141:     else
 142:     {
 143:       if (page < 0 || page > 7)
 144:       {
 145:         printf("page is 0,1,...,7\n");
 146:         return -1;
 147:       }
 148:       ioctl(fd, OLED_CMD_CLEAR_PAGE, page);
 149:     }
 150:   }
 151:   else if (do_show)
 152:   {
 153:     if (page < 0 || page > 7)
 154:     {
 155:       printf("page is 0,1,...,7\n");
 156:       return -1;
 157:     }
 158:     if (col < 0 || col > 127)
 159:     {
 160:       printf("col is 0,1,...,127\n");
 161:       return -1;
 162:     }
 163:  
 164:     OLEDPrint(fd, page, col, argv[3]);
 165:   }
 166:   return 0;
 167: }
 168:  


 

SPI驱动实现之软件控制器

一、驱动框架

image

从图中可以看出,只替换了两个部分,在硬件上使用几个GPIO,不再使用SPI硬件控制器,所以在驱动上也需要做相应的变更,这部分在kernel中已经支持了。

二、代码

下面我们只列一下不同的部分。

SPI GPIO软件控制器

这里采用的也是platform架构。

1、platform_device
  1: static struct spi_gpio_platform_data s3c_spi0_gpio_info = {
  2:   .num_chipselect = S3C_GPIO_END,
  3:   .miso    = S3C2410_GPE(11),
  4:   .mosi    = S3C2410_GPE(12),
  5:   .sck    = S3C2410_GPE(13),
  6: };
  7:  
  8: static struct platform_device s3c_device_spi0_gpio = {
  9:   .name    = "spi_gpio",
 10:   .id    = 0,
 11:   .dev    = {
 12:     .platform_data    = (void *)&s3c_spi0_gpio_info,
 13:   }
 14: };
 15:  
 16: static struct platform_device *tq2440_devices[] __initdata = {
 17: ......
 18:   &s3c_device_spi0_gpio
 19: };
 20:  
 21: static void __init tq2440_machine_init(void)
 22: {
 23: ......
 24:   platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));
 25: ......
 26: }
 27:  
 28: MACHINE_START(TQ2440, "TQ2440")
 29: ......
 30:   .init_machine  = tq2440_machine_init,
 31: ......
 32: MACHINE_END


第3/4/5行,表示需要spi软件控制器需要使用的gpio引脚,至少需要MISO、SCK、MOSI;

第10行,表示模拟出的spi软件控制器的编号,也就是spi总线编号;

第9行,将来会跟驱动中的name进行匹配;

2、platform_driver

文件:drivers/spi/spi-gpio.c

  1: #define DRIVER_NAME  "spi_gpio"
  2: ......
  3:  
  4: static struct platform_driver spi_gpio_driver = {
  5:   .driver = {
  6:     .name  = DRIVER_NAME,
  7:     .owner  = THIS_MODULE,
  8:     .of_match_table = of_match_ptr(spi_gpio_dt_ids),
  9:   },
 10:   .probe    = spi_gpio_probe,
 11:   .remove    = spi_gpio_remove,
 12: };
 13: module_platform_driver(spi_gpio_driver);


 

OLED驱动

下面只列出需要注意的地方。

1、OLED板级信息
  1: /* SPI OLED */
  2: static struct spi_board_info tq2440_spi_board_info[] __initdata = {
  3:   {
  4:     .modalias  = "oled",
  5:     .max_speed_hz  = 10000000,
  6:     .bus_num  = 0,
  7:     .mode    = SPI_MODE_0,
  8:     .chip_select  = S3C2410_GPG(1),
  9:     .platform_data  = (const void *)S3C2410_GPF(3),
 10: #ifdef CONFIG_TQ2440_USE_SPI_GPIO
 11:     .controller_data= (void *)S3C2410_GPG(1),
 12: #endif
 13:   },
 14: };


第11行,这个表示片选信号,具体参见drivers/spi/spi-gpio.c的实现;

内核配置

System Type  --->

    SAMSUNG S3C24XX SoCs Support  --->

        [*]   TQ2440 use spi gpio to communicate with peripherals

Device Drivers  --->

    [*] SPI support  --->

        <*>   GPIO-based bitbanging SPI Master

        <*>   Support TQ2440 OLED (from 100ask.com) 

测试

编译app

arm-linux-gcc -Wall oled_test.c -o oled_test


操作

  1: [root@TQ2440 sky]# ./oled_test init
  2: [root@TQ2440 sky]# ./oled_test clear
  3: [root@TQ2440 sky]# ./oled_test 0 0 "pengdonglin137"
  4: [root@TQ2440 sky]# 


结果

IMG_20150726_120551

 

用Saleae16分析SPI时序

IMG_20150726_094155

上面我们在设置oled板级信息的时候将spi通信的速率设置为了10MHz,我在抓取spi波形的时候,遇到了问题。

现象如下:

image

上面的图中,CLOCK时钟有些异常,可以看到只抓到7个波形,并且波形不是很均匀,出现很多类似的波形。刚开始我还以为spi控制器出问题了,后来发现,原来我把采样频率从16M提高到50M以后,全都正常了。

image

我想就是采用率太低的可能,记得有一个香农采样定理,采样信号的频率至少应该是被采信号的两倍。为了印证这个看法,我又做了下面几个测试。

1、将采样频率设置为25M,通信速率为10M

image

整个波形都没有问题。

2、将采样频率设置为16M,将通讯速率设置为7M

image

可以看到,至少抓到的还是8个波形,还算正常。

因此,基本验证了我的看法。

 

完。