4812|3

1379

帖子

2

TA的资源

五彩晶圆(初级)

楼主
 

NUCLEO-F410RB 测评第二周之ADC, I2S [复制链接]

本帖最后由 cruelfox 于 2015-12-13 13:05 编辑

一直还没用过STM32系列的ADC, 这次拿Nucleo上面的ADC采一下看看结果怎么样。F410的ADC最快可以到2Msps的采样率,我这次暂且只用100k的采样率来测试。100ksps对于单片机来说也够快的了,也没有什么处理工作做,仅把数据从I2S传出而已。

F410的ADC可以一次启动做连续的选定若干个通道转换,可以用定时器来触发启动,对多通道数据采集是有好处的。不过我就用一个通道了,开始是按Timer1 CC2事件触发来写的程序,实验到最后又改为软件触发了(原因后面说)。用Timer触发不需要额外的中断,只要把比较模式输出(当作PWM那么去做),但不用配置GPIO,就可以了。AD转换完成后可以使用中断来处理结果,也可以程序自由安排什么时候处理数据。F410 ADC的通道是映射到固定的IO pin上的,要把该GPIO pin设置成Analog功能(MODER寄存器中)。我先以每秒4次转换的速度把ADC结果读出来,从串口输出,发现数值跳动很大。以前用AVR的10位ADC,是可以做到跳动1个字的,呃……

继续做高采样速度的吧。串口的传输速度是不够用的了,所以用SPI/I2S口来做。STM32系列的I2S功能是和SPI共享硬件的,用起来就发现有些限制不是很爽:一是时钟问题,MCLK必须从I2SCLK分频出来,至少要4分频,而I2SCLK除非片用外时钟源,否则实现通用的采样率误差大;二是每次访问都只有固定的16-bit字长,一个采样周期要读或写4次,还要自个判断读写的是哪部分;三是只有一条串行数据线,半双工。我用I2S来传输是想利用现有的音频硬件工具,采集和传输到PC上方便。因为I2S有固定的时钟,ADC采样严格同步的话就形成音频流数据了。在Nucleo上面ADC, I2S都用一个主时钟,同步不成问题。

事先的设计是Timer触发ADC,然后ADC用DMA传输数据给I2S输出,做到最后需要操作DAC了我才发现DMA并没有设备到设备的模式,必须是设备和内存之间交换。F410的DMA操作和F0xx系列还不一样,今天没时间折腾了就放弃吧。100kHz采样率还好,16MHz的CPU频率就用中断来搞定应该问题不大。于是程序又简化了,ADC在SPI/I2S的中断中去软件启动,Timer也不用了。12-bit的结果用I2S左声道的前16位传输,后面3个WORD都写0。

照片上右边那块PCB是我的FPGA板,解析I2S数据并通过高速USB传输至PC。查看结果就用我惯用的CoolEdit了

这是ADC输入接了一节旧碱性电池的:

跳动范围有5个bit了。用CoolEdit的统计功能看,噪声水平还是很高的了

-79.4dB 是当作 16-bit 满幅度来看的,实际上ADC只有12-bit, 所以噪声是 -79.4+24=-55.4dB

再看看1kHz正弦波转换的结果

看时域波形很不光滑,DFT结果看下呢:

DFT结果看起来还行。看来就是ADC噪声比较大了,不知道单独设计PCB会不会好点。

程序在这里
  1. #include<string.h>

  2. #include "stm32f4xx.h"

  3. void setup_adc(void);
  4. void setup_i2s(void);


  5. int main(void)
  6. {

  7.     RCC->PLLCFGR = 0x2F001010;  // VCO = HSI/16*64, sys=VCO/2
  8.     RCC->CR |= RCC_CR_PLLON;
  9.     while(!(RCC->CR & RCC_CR_PLLRDY));

  10. //  FLASH->ACR = FLASH_ACR_ICEN|FLASH_ACR_DCEN; // no significance

  11.     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN|RCC_AHB1ENR_GPIOBEN;    // enable GPIO ports clock
  12.     __NOP();

  13.     GPIOA->MODER = GPIO_MODER_MODER14_1|GPIO_MODER_MODER13_1   // PA14, PA13 AF (SWD pins)
  14.                 |GPIO_MODER_MODER15_1|GPIO_MODER_MODER9_1   // PA15 (UART1-TX), PA9 (TIM1_CH2) AF
  15.                 |GPIO_MODER_MODER5_0    // PA5 as general output (LED)
  16.                 |GPIO_MODER_MODER0|GPIO_MODER_MODER1;   // analog function PA0, PA1
  17.     GPIOA->AFR[1] = 0x70000010; // AF1 for PA9, AF7 for PA15; AF0 for others

  18.     GPIOB->MODER = GPIO_MODER_MODER15_1|GPIO_MODER_MODER13_1|GPIO_MODER_MODER12_1;  // AF for I2S2
  19.     GPIOB->AFR[1] = 0x50550000; // AF5 for PB12,13,15 (I2S2 DAT, BCK, LRCK)

  20.     uart_setup();
  21.     uart_wstr("Hello from F410\r\n");
  22.     setup_i2s();
  23.     NVIC_EnableIRQ(SPI2_IRQn);

  24.     setup_adc();

  25.     while(1)
  26.     {
  27.         __WFI();
  28. /*      GPIOA->BSRRL = (1<<5);
  29.         __WFE();
  30.         GPIOA->BSRRH = (1<<5);  */
  31.     }
  32. }

  33. void setup_adc(void)
  34. {
  35.     RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
  36.     __NOP();
  37.     ADC->CCR = 0; // default: ADCPRE=0, ADC clock = PCLK/2
  38.     ADC1->CR1 = ADC_CR1_DISCEN; // discontinuous mode enable
  39.     ADC1->SQR3 = ADC_SQR3_SQ1&1;                    // only 1st conversion in sequence, Ch 1
  40.     ADC1->CR2 = ADC_CR2_ADON;               // enable
  41. //  ADC1->CR2 = ADC_CR2_ALIGN|ADC_CR2_ADON;             // enable
  42. }

  43. void setup_i2s(void)
  44. {
  45.     RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; // enable SPI2 (I2S) clock
  46.     __NOP();
  47.     SPI2->I2SCFGR = SPI_I2SCFGR_I2SMOD|SPI_I2SCFGR_I2SCFG_1|SPI_I2SCFGR_CHLEN; // I2S 16-bit master TX
  48.     SPI2->I2SPR = SPI_I2SPR_ODD|2;  // I2S prescaler, 32MHz/5=6.4MHz BCK, 100kHz FS
  49.     SPI2->CR2 = SPI_CR2_TXEIE;
  50.     SPI2->I2SCFGR |= SPI_I2SCFGR_I2SE;  // enable I2S
  51. }

  52. void SPI2_IRQHandler(void)
  53. {
  54.     static char p_side;
  55.     char side;
  56.     side=((SPI2->SR & SPI_SR_CHSIDE)!=0);
  57.     if(side==0 && p_side==1)
  58.     {
  59.         SPI2->DR = ADC1->DR;        // ^0x8000; // unsigned conversion
  60.         ADC1->CR2 |= ADC_CR2_SWSTART;   // start conversion
  61.     }
  62.     else
  63.         SPI2->DR = 0;
  64.     p_side=side;
  65. }
复制代码

补充说一下,STM32F410 的 I2S 时钟源。在F0系列里面,I2SCLK是系统的主时钟,要改频率就得主时钟一起切换,局限很大。F410中有改进了,可以从外部提供时钟单独给I2S使用,也可以把PLL时钟的一路给I2S,总之是可以和系统的时钟分开。对Codec接口这样无疑是友好不少。

在我上面贴的代码里面,I2S模块使用的是32MHz的PLL时钟,所以才能产生6.4MHz的CK及100kHz的LRCK。系统运行仍然是默认的16MHz的HSI时钟。


此帖出自stm32/stm8论坛

最新回复

楼主,看你DIY了好多ADC采集卡, 你可以反过来搞,DAC,再接换上power IC(也有直接数字输入的), 加上电路 直接一小型功放出来了。  详情 回复 发表于 2016-6-18 22:38

赞赏

2

查看全部赞赏

点赞 关注
 

回复
举报

267

帖子

0

TA的资源

一粒金砂(高级)

沙发
 
赞一个,很专业研究很深入!
此帖出自stm32/stm8论坛
 
个人签名

gitee/casy

 

回复

1万

帖子

24

TA的资源

版主

板凳
 
很专业。
此帖出自stm32/stm8论坛
 
 

回复

1950

帖子

4

TA的资源

版主

4
 

楼主,看你DIY了好多ADC采集卡,
你可以反过来搞,DAC,再接换上power IC(也有直接数字输入的), 加上电路
直接一小型功放出来了。
此帖出自stm32/stm8论坛
 
个人签名MicroPython中文社区https://micropython.org.cn/forum/  
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表