【练习贴:网上找的例程,改了改---树莓派SMI_DMA_读取AD9236】

最近一直在折腾AD转换,以前用NI的MCC118做了一个项目,应为MCC118有Python的库所以很容易就实现了。详情请看:使用 MCC 118 对交流电进行监测分析 | 树莓派实验室

但是MCC118单通道采样速率最高100K,一直想找一个高采样率的板子,但是都没有找到有Python库的,偶然在X宝上发现了一个AD9226的板子,国外有大神用树莓派SMI_DMA读取,详情请看:Raspberry Pi Secondary Memory Interface (SMI) – Lean2

但是用C语言编的程序,没办法只能硬着头皮学一学C了(都50岁了)。终于测试通过,特此留存纪念,并分享给大家。不知道怎么上传附件,原始程序就从大神的文章里找链接,到Github下载吧。我自己的程序,就把代码全部贴出来吧。

在文件所在目录编译文件:

gcc rpi_smi_adc_ok.c rpi_dma_utils.c -o adc_ok

rpi_smi_adc_ok.c  是我测试好的,rpi_smi_adc_test.c 是原来的程序,有点儿错误,大家可以自己找一找。

然后运行:

sudo ./adc_ok  25M >121-25000.txt

就输出采集结果,这是25M采样率的,原文里说AD转换器和树莓派的连线应小于5cm,我用的杜邦线20cm,测试最高25M的采样率没问题,30M就乱套了。单词采样最多30000点,再多了也乱套,正在研究如何无限采集点。

注意:原文里AD9226的  低位是  D11,我测试了同时查了Datasheet,AD9236 低位是  D0;这两个是反的,同时AD9236请使用。

// rpi_smi_dma_ok.c
//Test of parallel AD9226 ADC using Raspberry Pi SMI (Secondary Memory Interface)
// For detailed description, see https://iosoft.blog
//
// Copyright (c) 2020 Jeremy P Bentham
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// v0.06 JPB 16/7/20 Tidied up for Github

#include <stdio.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "rpi_dma_utils.h"
#include "rpi_smi_defs.h"

// Set zero for single value, non-zero for block read
#define USE_DMA         1
// Use test pin in place of GPIO mode setting (to check timing)
#define USE_TEST_PIN    0

// SMI cycle timings
#define SMI_NUM_BITS    SMI_16_BITS
//#define SMI_TIMING      SMI_TIMING_20M

//#if PHYS_REG_BASE==PI_4_REG_BASE        // Timings for RPi v4 (1.5 GHz)
//#define SMI_TIMING_1M   10, 38, 74, 38  // 1 MS/s
//#define SMI_TIMING_10M   6,  6, 13,  6  // 10 MS/s
//#define SMI_TIMING_20M   4,  5,  9,  5  // 19.74 MS/s
//#define SMI_TIMING_25M   4,  3,  8,  4  // 25 MS/s
//#define SMI_TIMING_31M   4,  3,  6,  3  // 31.25 MS/s
//#else                                   // Timings for RPi v0-3 (1 GHz)
//#define SMI_TIMING_1M   10, 25, 50, 25  // 1 MS/s
//#define SMI_TIMING_10M   4,  6, 13,  6  // 10 MS/s
//#define SMI_TIMING_20M   2,  6, 13,  6  // 20 MS/s
//#define SMI_TIMING_25M   2,  5, 10,  5  // 25 MS/s
//#define SMI_TIMING_31M   2,  4,  6,  4  // 31.25 MS/s
//#define SMI_TIMING_42M   2,  3,  6,  3  // 41.66 MS/s
//#define SMI_TIMING_50M   2,  3,  5,  2  // 50 MS/s
//#endif

// Number of raw bytes per ADC sample
#define SAMPLE_SIZE     2

// Number of samples to be captured, and number to be discarded
#define NSAMPLES        30000
#define PRE_SAMP        100

// Voltage calibration
#define ADC_ZERO        2080
#define ADC_SCALE       410.0

// GPIO pin numbers
#define ADC_D0_PIN      12
#define ADC_NPINS       12
#define SMI_SOE_PIN     6
#define SMI_SWE_PIN     7
#define SMI_DREQ_PIN    24
#define TEST_PIN        25

// DMA request threshold
#define REQUEST_THRESH  4

// SMI register names for diagnostic print
char *smi_regstrs[] = {
    "CS","LEN","A","D","DSR0","DSW0","DSR1","DSW1",
    "DSR2","DSW2","DSR3","DSW3","DMC","DCS","DCA","DCD",""
};

// SMI CS register field names for diagnostic print
#define STRS(x)     STRS_(x) ","
#define STRS_(...)  #__VA_ARGS__
char *smi_cs_regstrs = STRS(SMI_CS_FIELDS);
int strcmp(const char *s1,const char *s2);

// Structures for mapped I/O devices, and non-volatile memory
extern MEM_MAP gpio_regs, dma_regs;
MEM_MAP vc_mem, clk_regs_, smi_regs;

// Pointers to SMI registers
volatile SMI_CS_REG  *smi_cs;
volatile SMI_L_REG   *smi_l;
volatile SMI_A_REG   *smi_a;
volatile SMI_D_REG   *smi_d;
volatile SMI_DMC_REG *smi_dmc;
volatile SMI_DSR_REG *smi_dsr;
volatile SMI_DSW_REG *smi_dsw;
volatile SMI_DCS_REG *smi_dcs;
volatile SMI_DCA_REG *smi_dca;
volatile SMI_DCD_REG *smi_dcd;

// Buffer for captured samples
uint16_t sample_data[NSAMPLES];

// Non-volatile memory size
#define VC_MEM_SIZE(nsamp) (PAGE_SIZE + ((nsamp)+4)*SAMPLE_SIZE)

void map_devices(void);
void fail(char *s);
void terminate(int sig);
void smi_start(int nsamples, int packed);
uint32_t *adc_dma_start(MEM_MAP *mp, int nsamp);
int adc_dma_end(void *buff, uint16_t *data, int nsamp);
void init_smi(int width, int ns, int setup, int hold, int strobe);
void disp_smi(void);
void mode_word(uint32_t *wp, int n, uint32_t mode);
float val_volts(int val);
int adc_gpio_val(void);
void disp_reg_fields(char *regstrs, char *name, uint32_t val);
void dma_wait(int chan);

int main(int argc, char *argv[])
{
    void *rxbuff;
    int i;


    signal(SIGINT, terminate);
    map_devices();
    for (i=0; i<ADC_NPINS; i++)
        gpio_mode(ADC_D0_PIN+i, GPIO_IN);
    gpio_mode(SMI_SOE_PIN, GPIO_ALT1);
#if !USE_DMA
    init_smi(SMI_NUM_BITS, SMI_TIMING_1M);
    while (1)
    {
        smi_start(PRE_SAMP, 1);
        usleep(20);
        int val = adc_gpio_val();
        printf("%4u %1.3f\n", val, val_volts(val));
        sleep(1);
    }
#else
if (strcmp(argv[1],"200k")== 0)
    init_smi(SMI_NUM_BITS,30,62,126,62);		//200K
else if (strcmp(argv[1],"250k")== 0)
    init_smi(SMI_NUM_BITS,30,50,100,50);		//250K
else if (strcmp(argv[1],"300k")== 0)
    init_smi(SMI_NUM_BITS,20,62,126,62);		//300K
else if (strcmp(argv[1],"400k")== 0)
    init_smi(SMI_NUM_BITS,15,65,120,65);		//400K
else if (strcmp(argv[1],"500k")== 0)
    init_smi(SMI_NUM_BITS,15,50,100,50);		//500K
else if (strcmp(argv[1],"750k")== 0)
    init_smi(SMI_NUM_BITS,10,50,100,50);		//750K	
else if (strcmp(argv[1],"1M")== 0)
    init_smi(SMI_NUM_BITS,15,25,50,25);			//1M
else if (strcmp(argv[1],"1.5M")== 0||strcmp(argv[1],"1M5")== 0)
    init_smi(SMI_NUM_BITS,10,25,50,25);			//1.5M
else if (strcmp(argv[1],"2M")== 0)
    init_smi(SMI_NUM_BITS,5,38,76,38);			//2M	
else if (strcmp(argv[1],"2.5M")== 0||strcmp(argv[1],"2M5")== 0)	
    init_smi(SMI_NUM_BITS,5,30,60,30);			//2.5M
else if (strcmp(argv[1],"3M")== 0)
    init_smi(SMI_NUM_BITS,5,25,50,25);			//3M
else if (strcmp(argv[1],"4M")== 0)
    init_smi(SMI_NUM_BITS,5,18,39,18);			//4M
else if (strcmp(argv[1],"5M")== 0)
    init_smi(SMI_NUM_BITS,5,15,30,15);			//5M
else if (strcmp(argv[1],"7.5M")== 0||strcmp(argv[1],"7M5")== 0)
    init_smi(SMI_NUM_BITS,5,10,20,10);			//7.5M	
else if (strcmp(argv[1],"10M")== 0)
    init_smi(SMI_NUM_BITS,6,6,12,7);			//10M
else if (strcmp(argv[1],"15M")== 0)
    init_smi(SMI_NUM_BITS,4,6,12,7);			//15M
else if (strcmp(argv[1],"20M")== 0)
    init_smi(SMI_NUM_BITS,5,4,7,4);			//20M
else if (strcmp(argv[1],"25M")== 0)
    init_smi(SMI_NUM_BITS,4,3,8,4);			//25M
else
    init_smi(SMI_NUM_BITS,15,25,50,25);			//1M
		

		
#if USE_TEST_PIN
    gpio_mode(TEST_PIN, GPIO_OUT);
    gpio_out(TEST_PIN, 0);
#endif
    map_uncached_mem(&vc_mem, VC_MEM_SIZE(NSAMPLES+PRE_SAMP));
    smi_dmc->dmaen = 1;
    smi_cs->enable = 1;
    smi_cs->clear = 1;
    rxbuff = adc_dma_start(&vc_mem, NSAMPLES);
    smi_start(NSAMPLES, 1);
    while (dma_active(DMA_CHAN_A)) ;
    adc_dma_end(rxbuff, sample_data, NSAMPLES);
    disp_reg_fields(smi_cs_regstrs, "CS", *REG32(smi_regs, SMI_CS));
    smi_cs->enable = smi_dcs->enable = 0;
    for (i=0; i<NSAMPLES; i++)
        printf("%1.3f\n", val_volts(sample_data[i]));
#endif
    terminate(0);
    return(0);
}

// Map GPIO, DMA and SMI registers into virtual mem (user space)
// If any of these fail, program will be terminated
void map_devices(void)
{
    map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE);
    map_periph(&dma_regs, (void *)DMA_BASE, PAGE_SIZE);
    map_periph(&clk_regs_, (void *)CLK_BASE, PAGE_SIZE);
    map_periph(&smi_regs, (void *)SMI_BASE, PAGE_SIZE);
}

// Catastrophic failure in initial setup
void fail(char *s)
{
    printf(s);
    terminate(0);
}

// Free memory segments and exit
void terminate(int sig)
{
    int i;

    printf("Closing\n");
    if (gpio_regs.virt)
    {
        for (i=0; i<ADC_NPINS; i++)
            gpio_mode(ADC_D0_PIN+i, GPIO_IN);
    }
    if (smi_regs.virt)
        *REG32(smi_regs, SMI_CS) = 0;
    stop_dma(DMA_CHAN_A);
    unmap_periph_mem(&vc_mem);
    unmap_periph_mem(&smi_regs);
    unmap_periph_mem(&dma_regs);
    unmap_periph_mem(&gpio_regs);
    exit(0);
}

// Start SMI, given number of samples, optionally pack bytes into words
void smi_start(int nsamples, int packed)
{
    smi_l->len = nsamples + PRE_SAMP;
    smi_cs->pxldat = (packed != 0);
    smi_cs->enable = 1;
    smi_cs->clear = 1;
    smi_cs->start = 1;
}

// Start DMA for SMI ADC, return Rx data buffer
uint32_t *adc_dma_start(MEM_MAP *mp, int nsamp)
{
    DMA_CB *cbs=mp->virt;
    uint32_t *data=(uint32_t *)(cbs+4), *pindata=data+8, *modes=data+0x10;
    uint32_t *modep1=data+0x18, *modep2=modep1+1, *rxdata=data+0x20, i;

    // Get current mode register values
    for (i=0; i<3; i++)
        modes[i] = modes[i+3] = *REG32(gpio_regs, GPIO_MODE0 + i*4);
    // Get mode values with ADC pins set to SMI
    for (i=ADC_D0_PIN; i<ADC_D0_PIN+ADC_NPINS; i++)
        mode_word(&modes[i/10], i%10, GPIO_ALT1);
    // Copy mode values into 32-bit words
    *modep1 = modes[1];
    *modep2 = modes[2];
    *pindata = 1 << TEST_PIN;
    enable_dma(DMA_CHAN_A);
    // Control blocks 0 and 1: enable SMI I/P pins
    cbs[0].ti = DMA_SRCE_DREQ | (DMA_SMI_DREQ << 16) | DMA_WAIT_RESP;
#if USE_TEST_PIN
    cbs[0].tfr_len = 4;
    cbs[0].srce_ad = MEM_BUS_ADDR(mp, pindata);
    cbs[0].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_SET0);
    cbs[0].next_cb = MEM_BUS_ADDR(mp, &cbs[2]);
#else
    cbs[0].tfr_len = 4;
    cbs[0].srce_ad = MEM_BUS_ADDR(mp, modep1);
    cbs[0].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_MODE0+4);
    cbs[0].next_cb = MEM_BUS_ADDR(mp, &cbs[1]);
#endif
    cbs[1].tfr_len = 4;
    cbs[1].srce_ad = MEM_BUS_ADDR(mp, modep2);
    cbs[1].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_MODE0+8);
    cbs[1].next_cb = MEM_BUS_ADDR(mp, &cbs[2]);
    // Control block 2: read data
    cbs[2].ti = DMA_SRCE_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_DEST_INC;
    cbs[2].tfr_len = (nsamp + PRE_SAMP) * SAMPLE_SIZE;
    cbs[2].srce_ad = REG_BUS_ADDR(smi_regs, SMI_D);
    cbs[2].dest_ad = MEM_BUS_ADDR(mp, rxdata);
    cbs[2].next_cb = MEM_BUS_ADDR(mp, &cbs[3]);
    // Control block 3: disable SMI I/P pins
    cbs[3].ti = DMA_CB_SRCE_INC | DMA_CB_DEST_INC;
#if USE_TEST_PIN
    cbs[3].tfr_len = 4;
    cbs[3].srce_ad = MEM_BUS_ADDR(mp, pindata);
    cbs[3].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_CLR0);
#else
    cbs[3].tfr_len = 3 * 4;
    cbs[3].srce_ad = MEM_BUS_ADDR(mp, &modes[3]);
    cbs[3].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_MODE0);
#endif
    start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
    return(rxdata);
}

// ADC DMA is complete, get data
int adc_dma_end(void *buff, uint16_t *data, int nsamp)
{
    uint16_t *bp = (uint16_t *)buff;
    int i;

    for (i=0; i<nsamp+PRE_SAMP; i++)
    {
        if (i >= PRE_SAMP)
            *data++ = bp[i] >> 4;
    }
    return(nsamp);
}

// Initialise SMI, given data width, time step, and setup/hold/strobe counts
// Step value is in nanoseconds: even numbers, 2 to 30
void init_smi(int width, int ns, int setup, int strobe, int hold)
{
    int divi = ns / 2;

    smi_cs  = (SMI_CS_REG *) REG32(smi_regs, SMI_CS);
    smi_l   = (SMI_L_REG *)  REG32(smi_regs, SMI_L);
    smi_a   = (SMI_A_REG *)  REG32(smi_regs, SMI_A);
    smi_d   = (SMI_D_REG *)  REG32(smi_regs, SMI_D);
    smi_dmc = (SMI_DMC_REG *)REG32(smi_regs, SMI_DMC);
    smi_dsr = (SMI_DSR_REG *)REG32(smi_regs, SMI_DSR0);
    smi_dsw = (SMI_DSW_REG *)REG32(smi_regs, SMI_DSW0);
    smi_dcs = (SMI_DCS_REG *)REG32(smi_regs, SMI_DCS);
    smi_dca = (SMI_DCA_REG *)REG32(smi_regs, SMI_DCA);
    smi_dcd = (SMI_DCD_REG *)REG32(smi_regs, SMI_DCD);
    smi_cs->value = smi_l->value = smi_a->value = 0;
    smi_dsr->value = smi_dsw->value = smi_dcs->value = smi_dca->value = 0;
    if (*REG32(clk_regs_, CLK_SMI_DIV) != divi << 12)
    {
        *REG32(clk_regs_, CLK_SMI_CTL) = CLK_PASSWD | (1 << 5);
        usleep(10);
        while (*REG32(clk_regs_, CLK_SMI_CTL) & (1 << 7)) ;
        usleep(10);
        *REG32(clk_regs_, CLK_SMI_DIV) = CLK_PASSWD | (divi << 12);
        usleep(10);
        *REG32(clk_regs_, CLK_SMI_CTL) = CLK_PASSWD | 6 | (1 << 4);
        usleep(10);
        while ((*REG32(clk_regs_, CLK_SMI_CTL) & (1 << 7)) == 0) ;
        usleep(100);
    }
    if (smi_cs->seterr)
        smi_cs->seterr = 1;
    smi_dsr->rsetup = smi_dsw->wsetup = setup;
    smi_dsr->rstrobe = smi_dsw->wstrobe = strobe;
    smi_dsr->rhold = smi_dsw->whold = hold;
    smi_dmc->panicr = smi_dmc->panicw = 8;
    smi_dmc->reqr = smi_dmc->reqw = REQUEST_THRESH;
    smi_dsr->rwidth = smi_dsw->wwidth = width;
}

// Display SMI registers
void disp_smi(void)
{
    volatile uint32_t *p=REG32(smi_regs, SMI_CS);
    int i=0;

    while (smi_regstrs[i][0])
    {
        printf("%4s=%08X ", smi_regstrs[i++], *p++);
        if (i%8==0 || smi_regstrs[i][0]==0)
            printf("\n");
    }
}

// Get GPIO mode value into 32-bit word
void mode_word(uint32_t *wp, int n, uint32_t mode)
{
    uint32_t mask = 7 << (n * 3);

    *wp = (*wp & ~mask) | (mode << (n * 3));
}

// Convert ADC value to voltage
float val_volts(int val)
{
    return((ADC_ZERO - val) / ADC_SCALE);
}

// Return ADC value, using GPIO inputs
int adc_gpio_val(void)
{
    int v = *REG32(gpio_regs, GPIO_LEV0);

    return((v>>ADC_D0_PIN) & ((1 << ADC_NPINS)-1));
}

// Display bit values in register
void disp_reg_fields(char *regstrs, char *name, uint32_t val)
{
    char *p=regstrs, *q, *r=regstrs;
    uint32_t nbits, v;

    printf("%s %08X", name, val);
    while ((q = strchr(p, ':')) != 0)
    {
        p = q + 1;
        nbits = 0;
        while (*p>='0' && *p<='9')
            nbits = nbits * 10 + *p++ - '0';
        v = val & ((1 << nbits) - 1);
        val >>= nbits;
        if (v && *r!='_')
            printf(" %.*s=%X", q-r, r, v);
        while (*p==',' || *p==' ')
            p = r = p + 1;
    }
    printf("\n");
}

// EOF

接线:树莓派400  +   AD9236

信号发生器   

1kHz信号200k采样率,1000点波形 

1kHz信号500k采样率,1000点波形

1kHz信号1M采样率,1000点波形

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值