500 kHz DSO mit dem CH32V003          


Elektronik-Labor  Projekte  CH32V


Ich wollte immer schon mal ein schnelles Scope mit DMA (Direct Memory Access) bauen, aber bei den Controllern, die es können, erschien mir die Sache zu kompliziert. Jetzt hatte ich erstmalig eine echte Chance, und zwar durch die guten Beispielprojekte des CH32V003F4P6, hier speziell das Projekt ADC_DMA. Durch kleine Veränderungen kam ich Stück für Stück dem Ziel näher. Ich konnte mit wenig Aufwand verschiedene Parameter ausprobieren, insbesondere die ADC-Taktfrequenz und die Sample-Zeit. Die gegenüber dem Beispiel veränderten Zeilen habe ich markiert.

/********************************** (C) COPYRIGHT *******************************
 * File Name          : main.c
 * Author             : WCH
 * Version            : V1.0.0
 * Date               : 2022/08/08
 * Description        : Main program body.
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/

/*
 *@Note
 *ADC DMA sampling routine:
 *ADC channel 2 (PC4), the rule group channel obtains ADC conversion data for 10 consecutive
 *times through DMA.
 *Geändert auf 500 kHz, 500 Samples für Arduino Plotter
 *
*/

#include "debug.h"

/* Global Variable */
u16 TxBuf[500]; //neu

/*********************************************************************
 * @fn      ADC_Function_Init
 *
 * @brief   Initializes ADC collection.
 *
 * @return  none
 */
void ADC_Function_Init(void)
{
    ADC_InitTypeDef  ADC_InitStructure = {0};
    GPIO_InitTypeDef GPIO_InitStructure = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div4);       //neu 8>4

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    ADC_DeInit(ADC1);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    ADC_Calibration_Vol(ADC1, ADC_CALVOL_50PERCENT);
    ADC_DMACmd(ADC1, ENABLE);
    ADC_Cmd(ADC1, ENABLE);

    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

/*********************************************************************
 * @fn      DMA_Tx_Init
 *
 * @brief   Initializes the DMAy Channelx configuration.
 *
 * @param   DMA_CHx - x can be 1 to 7.
 *          ppadr - Peripheral base address.
 *          memadr - Memory base address.
 *          bufsize - DMA channel buffer size.
 *
 * @return  none
 */
void DMA_Tx_Init(DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize)
{
    DMA_InitTypeDef DMA_InitStructure = {0};

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA_CHx);
    DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
    DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = bufsize;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_CHx, &DMA_InitStructure);
}

/*********************************************************************
 * @fn      main
 *
 * @brief   Main program.
 *
 * @return  none
 */
int main(void)
{
    u16 i;
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);
    ADC_Function_Init();
while(1){ //neu

    DMA_Tx_Init(DMA1_Channel1, (u32)&ADC1->RDATAR, (u32)TxBuf, 500); //neu
    DMA_Cmd(DMA1_Channel1, ENABLE);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);  //neu 15 minimal, Clk/4 500 kHz
    //ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_241Cycles);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    Delay_Ms(50);
    ADC_SoftwareStartConvCmd(ADC1, DISABLE);

    for(i = 0; i < 500; i++){ //neu
        printf("%04d\r\n", TxBuf[i]);
        //Delay_Ms(10);
    }
    Delay_Ms(2000);
}
}  //Abtastrate 500 kHz


Zur Darstellung der Messwerte habe ich den Arduino-Plotter eingesetzt. Hier wird ein 10-kHz-Sinussignal mit der Auflösung von 10 Bit und einer Breite von 500 Messpunkten gemessen. Insgesamt sieht man zehn Schwingungen, wobei jede aus 50 Messpunkten besteht. Daraus ergibt sich die Abtastrate von 500 kHz.



Das Signal kam von einem analogen Sinusgenerator. Parallel habe ich es mit meinem Hameg-Scope und einem Frequenzzähler gemessen. Beide bestätigen das Ergebnis. Ein 500-kHz-DSO mit einem Controller, der keine 30 Ct kostet.





Elektronik-Labor  Projekte  CH32V