CH32V003 Signalgenerator          


Elektronik-Labor  Projekte  CH32V


Beim Spielen mit den Programmbeispielen zum Timer bin ich auch auf die PWM-Ausgabe gestoßen. Man kann hier jeweils den Vorteiler, die Zählweite und die Impulsbreite vorgeben. Ich kann also problemlos ein PWM-Signal mit 100 Schritten erzeugen, sodass die Pulsbreite 50 genau für 50% steht. Oder ich beschränke den Umfang auf zwei Schritte und die Impulsbreite 1. Dann bekomme ich die höchste mögliche Frequenz von 24 MHz, wenn kein Vorteiler verwendet wird. Das erzeugte Rechtecksignal erscheint an PD2.

TIM1_PWMOut_Init( 1, 0, 1 );   //Kein Vorteiler, out 24 MHz

Das ist doch genau der richtige Weg für einen vielseitigen Signalgenerator. Allerdings muss die Software dann Eingaben entgegennehmen. Die meisten Beispiele verwenden zwar printf für serielle Ausgaben, aber die Gegenrichtung ist nicht so gut vorberietet. Geholfen hat mir das Beispiel USART_polling. Inzwischen habe ich auch bemerkt, dass man Programmteile von einem Beispiel gut in ein anderes kopieren kann. Sehr aufwendig ist meist die Initialisierung, weil es ungewohnt viele Betriebsmodi und Einstellungen für die Peripherieelemente gibt. Das kann lange dauern, bis ich die alle richtig verstehe, aber mit den Vorlagen komme ich schneller ans Ziel.

// Rechteckgenerator 400 Hz ...24 MHz

#include "debug.h"

volatile int n;
volatile int f;

void TIM1_PWMOut_Init(u16 arr, u16 psc, u16 ccp)
{
    GPIO_InitTypeDef GPIO_InitStructure={0};
    TIM_OCInitTypeDef TIM_OCInitStructure={0};
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure={0};

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOD | RCC_APB2Periph_TIM1, ENABLE );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_30MHz;
    GPIO_Init( GPIOD, &GPIO_InitStructure );

    TIM_TimeBaseInitStructure.TIM_Period = arr;
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit( TIM1, &TIM_TimeBaseInitStructure);

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = ccp;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init( TIM1, &TIM_OCInitStructure );

    TIM_CtrlPWMOutputs(TIM1, ENABLE );
    TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Disable );
    TIM_ARRPreloadConfig( TIM1, ENABLE );
    TIM_Cmd( TIM1, ENABLE );
}

void USARTx_CFG(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure = {0};
    USART_InitTypeDef USART_InitStructure = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1, ENABLE);

    // USART1 TX-->D.5   RX-->D.6
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_30MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
}

int getchar(void) {
    while((USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)){}      
    return (USART_ReceiveData(USART1));
}

uint16_t getint(void){
  char c;
  uint16_t n=0;
  while((USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)){}      
  c=(USART_ReceiveData(USART1));
  while (c > 13){
    if (c>47 && c<58){
      n=n*10;
      n+=c-48;
    }
    while((USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)){}      
    c=(USART_ReceiveData(USART1));
   }
   return n;
}

int main(void)
{
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n",SystemCoreClock);
    printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
    TIM1_PWMOut_Init( 1, 0, 1 );   //Kein Vorteiler, out 24 MHz
    USARTx_CFG();

    while(1)
    {
        n=getint();
        TIM1_PWMOut_Init(2*n-1, 0, n );      
        f=SystemCoreClock / n / 2;
        printf("%d Hz\r\n",f);
    }
}

Die Funktion getint() habe ich selbst hinzugefügt. Sie liefert das Ergebnis einer empfangenen 16-Bit-Zahl, also Werte von 0 bis 65535, genau den Umfang, den auch die Timer-Einstellungen bieten. Den Quelltext habe ich aus meinen PicoBasic-Projekten übernommen und angepasst. Die empfangene Zahl wird dann an die PWM-Funktion weitergegeben:

TIM1_PWMOut_Init(2*n-1, 0, n );

Im Terminal kann ich nun Zahlen absenden, die als Teiler von 24 MHz dienen. Wichtig ist, dass das Zeilenende mitgesendet wird, damit getint() weiß, wann die Eingabe vollständig ist. Mit der 1 bekomme ich 24 MHz, mit der 2  sind es 12 MHz usw. Je größer die  Eingabe, desto feiner werden die Frequenzschritte. Mit der gößten Eingabe 65535 komme ich bis herunter in den NF-Bereich auf 366 Hz. Quarzgenaue Signale von NF bis Kurzwelle, da gibt es viele Einsatzbereiche.


Elektronik-Labor  Projekte  CH32V