zoukankan      html  css  js  c++  java
  • 基于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:
    ");
      84:     printf("%s init
    ", cmd);
      85:     printf("%s clear
    ", cmd);
      86:     printf("%s clear <page>
    ", cmd);
      87:     printf("%s <page> <col> <string>
    ", cmd);
      88:     printf("eg:
    ");
      89:     printf("%s 2 0 100ask.taobao.com
    ", cmd);
      90:     printf("page is 0,1,...,7
    ");
      91:     printf("col is 0,1,...,127
    ");
      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
    ");
     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
    ");
     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
    ");
     156:             return -1;
     157:         }
     158:         if (col < 0 || col > 127)
     159:         {
     160:             printf("col is 0,1,...,127
    ");
     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]# 

    结果(使用SPI驱动的两种实现方式的实验现象是一样的,只是驱动的内部实现机理不同)

    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个波形,还算正常。

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

     

    完。

  • 相关阅读:
    <2014 04 29> *nix环境编程常用库总结
    <2014 04 29> c/c++常用库总结
    <2014 04 26> 《Coders at Work编程人生:15位软件先驱访谈录》
    <2014 04 16> 上班实习第一天
    <2014 04 15> C++语言回顾精要(原创By Andrew)
    [荐][转]为何应该使用 MacOS X(论GUI环境下开发人员对软件的配置与重用)
    [荐][转]王垠:我和权威的故事(2014)
    [荐][转]如何用美剧真正提升你的英语水平
    [转] 数学的用处(一)(二)(三)(四)(数学图谱)
    metadata 和 routing
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/4676866.html
Copyright © 2011-2022 走看看