Forum: Mikrocontroller und Digitale Elektronik Tiefpass-Spannung Filtern


von Robin (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich versuche seit einem Monat die Spannung die durch den Poti auf meinem 
mikrocontroller angezeigt wird zu reglen denn es zappelt an der letzte 
Ziffer um +/-1 und verusacht somit somit das Zappeln an der Position der 
Abgasegegendruckklappe (das Programm-das eigentlich wenn eine Spannung 
an mikrocontroller anliegt, interpretiert es als position und 
fährt/stellt die Abgasegegendruckklappe- habe ich nicht selber 
geschrieben aber ich muss das erweitern um dieses Zappeln wegzubekommen 
d.h die Spannung so filtern dass es nach dem Einstellen durch den Poti 
ein fester Wert zeigt und somit als feste position interpretiert 
wird)...Manchmal bleit eine gewisse Zeit die Spannung Konstant aber die 
Position ändert sich troztdem.
Ich bin kein expert in C (code ist in C geschrieben) auch nicht in 
Sachen Mikrocontroller aber ich dachte mir dass ich nur einen Tiefpass 
reinprogrammieren soll und das wärs aber es tut dann gar nicht filtern 
das Zappelt genauso wie vorher...Ich bin auch jetzt sehr neugierig wie 
ich das Problem lösen kann und für Eure Antworte/Fragen sehr dankbar.
PS: Beim Tiefpass() was soll ich da genauer beachten ?

Grüße Robin

: Verschoben durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Robin schrieb:
> ich dachte mir dass ich nur einen Tiefpass reinprogrammieren soll
> und das wärs aber es tut dann gar nicht filtern das Zappelt
> genauso wie vorher...
Wie sieht denn dein Code aus?
Meinst du nur das Zappeln auf dem letzten Bit?

> denn es zappelt an der letzte Ziffer um +/-1 und verusacht somit
> somit das Zappeln an der Position der Abgasegegendruckklappe
Ja, da ist dann wohl zuviel Verstärkung oder P-Anteil in der 
Regelstrecke. Denn auf das letzte Bit eines ADCs kann man sich nicht 
verlassen. Das steht aber im Datenblatt...

von Laura L. (laura_l)


Lesenswert?

hier ist das Hauptprogramm


//Pre-Processor Directives:
//Provide pre-processor directives to include Device header files and
//any application specific header files. Path locations to the Device
//header files are set up within the MPLAB 
Project>>BuildOptions>>Project
//dialog box. The path should point to X:\\MPLAB C30\support\h\, where
//"X:" is the folder where the MPLAB C30 tools are installed.
#include <p30fxxxx.h>   //"p30fxxxx.h" is a generic header file for 
dsPIC30F
                        //devices. This file will in turn select the 
actual
                        //device header file, based on the device 
selected
                        //by the MPLAB workspace/project. Using the 
p30fxxxx.h
                        //file makes it easy to migrate a project from 
one
                        //dsPIC device to another.
#define _USE_MATH_DEFINES
#include "math.h"
#include "system.h"
     //"system.h" is a header file defined for this
                        //application.

//Macros for Configuration Fuse Registers:
//Invoke macros to set up  device configuration fuse registers.
//The fuses will select the oscillator source, power-up timers, 
watch-dog
//timers, BOR characteristics etc. The macros are defined within the 
device
//header files. The configuration fuse registers reside in Flash memory.
_FOSC(CSW_FSCM_OFF & XT_PLL8);  //Run this project using an external 
crystal
                                //routed via the PLL in 8x multiplier 
mode
                                //For the 7.3728 MHz crystal we will 
derive a
                                //throughput of 7.3728*10^6*8/4 = 
14.7456 MIPS(Fcy)
                                //,~68 nanoseconds instruction cycle 
time(Tcy).
_FWDT(WDT_OFF);                 //Turn off the Watch-Dog Timer.
_FBORPOR(MCLR_EN & PWRT_OFF);   //Enable MCLR reset pin and turn off the
                                //power-up timers.
_FGS(CODE_PROT_OFF);            //Disable Code Protection

//Declaration to Link External Functions & Variables:
//Declare functions being used that are defined outside this file, or
//elsewhere in this MPLAB project.
extern SPI_Init(void);
extern UpdateDisplayBuffer(void);
//extern WriteUART_to_RS232(void);
extern WriteSPI_to_LCD(void);
//extern UART_Init(void);
extern ADC_Init(void);
extern INTx_Init(void);
extern Timer2_Init(void);
extern CAN_Init( int pos);
unsigned int position; //Variable für Stellerposition
extern int Potentiometer;  //Variable für ADC-Wert von dem Poti
unsigned int potivorher=0;

unsigned int geglaetteterwert=0;



//Functions and Variables with Global Scope:
//Declare functions in this file that have global scope.
int main (void);

//Code execution automatically reaches the main() function after
//two events have occurred;
//1. A Reset event triggered by hardware or software
//2. The execution of the C Start up library functions, present
//   in the crt0.o file in the libpic30-coff.a library file


unsigned int Tiefpass()

{

float i =0;
float  T=0.00001;
int fg=2000000;
float q,p,gefilterterwert;
q=2*3.14*fg*T;
p=exp(-q);
                  for (i=0;i<=T;i+0.0000005)
                  {
                    gefilterterwert=q*Potentiometer + p*potivorher;
                    potivorher=(int)gefilterterwert;

                  }
return potivorher;
}


int main (void)
{

        ADPCFG = 0xFFFF;        //After reset all port pins multiplexed
                                //with the A/D converter are configred 
analog.
                                //We will reconfigure them to be digital
                                //by writing all 1's to the ADPCFG 
register.
                                //Note: All dsPIC registers are memory 
mapped.
                                //The address of ADPCFG and other 
registers
                                //are defined in the device linker 
script
                                //in your project.

        //Function Delay5ms() available in file, Delay.s
        Delay5ms(100);          //Provide 500ms delay for the LCD to 
start-up.

        //Function SPI_Init() available in file, SPI_for_LCD.c
        SPI_Init();             //Initialize the SPI module to 
communicate with
                                //the LCD.

        //Function UART_Init() available in file, UART.c
        //UART_Init();            //Initialize the UART module to 
communicate
                                //with the COM port on the Host PC via 
an
                                //RS232 cable and the DB9 connector.

        //Function ADC_Init() available in file, A_to_D_Converter.c
        ADC_Init();             //Initialize the A/D converter to 
convert
                                //signals from the Temperature Sensor 
and the
                                //Potentiometer.

        //Function INTx_IO_Init() available in file, INTx_IO_pins.c
        INTx_IO_Init();         //Initialize the External interrupt pins 
and
                                //some I/O pins to accept input from the
                                //switches, S5 and S6 and drive the 
LEDs, D3
                                //and D4.

        //Function Timer1_Init() & Timer2_Init() available in file, 
Timers.c
        //Timer1_Init();          //Initialize Timer1 to provide 
"blinking" time
                                //for the LEDs.
        Timer2_Init();          //Initialize Timer2 and Timer3 as a 
32-bit
                                //Timer to be used for updating data 
sent to
                                //the LCD(SPI) and COM(UART) interfaces.


        while (1)               //Main Loop of Code Executes forever
        {            //Spannung 0-5 V am RB3, Umschaltung vom Poti auf 
Messgalgensignal: Jumper H13



unsigned int Potentiometer = potivorher;
CAN_Init(Potentiometer/4);  //Aufruf der CAN-Funktion zum Senden des 
Signals ADC-Wert/4 als Übergabeparameter (ADC_Wert: 0x000-0xFFF)
        //Die Position muss kontinuierlich über CAN geschickt werden
             position=Potentiometer/4;   //Variable Position für die 
Ausgabe am Display




                while (IFS0bits.T3IF == 1)      //Wait until 32-bit 
Timer
                {                               //interrupt flag bit is 
set.
                        IFS0bits.T3IF = 0;      //Clear 32-bit timer 
interrupt
                                                //flag bit
                        T2CONbits.TON = 0;      //Stop 32-bit Timer

                        //Function UpdateDisplayBuffer() available
                        //in file, DisplayRoutines.c
                        UpdateDisplayBuffer();  //Write the most recent
                                                //temperature and 
potentiometer
                                                //values into display 
buffer

                        //Function WriteSPI_to_LCD() in file, 
SPI_for_LCD.c
                        WriteSPI_to_LCD();      //Update the LCD via SPI

                        T2CONbits.TON = 1;      //Start 32-bit Timer 
again
                }
       }
        return 0;               //Code never reaches here!
}

von Wooschder (Gast)


Lesenswert?

Hi!

Ein Tiefpass bringt hier wenig. Durch den Tiefpass wird das Zappeln 
langsamer, aber weg geht es nicht.

Du kannst eine Hysterese einbauen. Das heißt du merkst dir den Wert des 
letzten Samples in einer Variablen und vergleichst ihn mit dem aktuellen 
gesampleten Wert. Wenn der Unterschied größer als 1 Bit ist, dann 
speicherst du den den aktuellen Wert in der Variable. Wenn der 
Unterschied kleiner ist, dann behälst du den Wert in der Variable bei.

Ansonsten hilft die Formartierung mit [ c] und [/c] (ohne Leerzeichen) 
ungemein bei der Lesbarkeit des Codes.

Gruß
Stefan

von Wooschder (Gast)


Lesenswert?

Nachtrag:

Wenn du die Hysterese verwendest, benötigst du keinen Tiefpass.

von Wooschder (Gast)


Lesenswert?

Nachnachtrag:

Wie lange dauert eine Periode des Timers bzw. Wie oft wird der ADC 
ausgelesen und das Display aktualisiert?

von Laura L. (laura_l)


Lesenswert?

Wooschder schrieb:
> Nachnachtrag:
>
> Wie lange dauert eine Periode des Timers bzw. Wie oft wird der ADC
> ausgelesen und das Display aktualisiert?

//Functions:

//ADC_Init() is used to configure A/D to scan and convert 2 input 
channels
//per interrupt. The A/D is set up for a total sampling rate of 8KHz
//or 4KHz per channel. The internal counter in the A/D is used to 
provide
//Acquisition time delay. The input pins being scanned are AN2 and AN3.
//AN2 and AN3 are connected to the Temperature Sensor and the 
Potentiometer
//on the dsPICDEM2 board.

void ADC_Init(void)
{
        //ADCON1 Register
        //Set up A/D for Automatic Sampling, Auto-Convert
        //All other bits to their default state
        ADCON1bits.SSRC = 7;
        ADCON1bits.ASAM = 1;

        //ADCON2 Register
        //Set up A/D for interrupting after 2 samples get filled in the 
buffer
        //Also, enable Channel scanning
        //All other bits to their default state
        ADCON2bits.SMPI = 1;
        ADCON2bits.CSCNA = 1;

        //ADCON3 Register
        //Set up Acquisition time (Tacq) for 31 Tad periods
        //where, Tad = A/D conversion clock time.
        //Set up Tad period to be 20.5 Tcy (Tcy = instruction cycle 
time)
        //Given that each conversion takes 14*Tad (=Tconv) periods,
        //Total Sample Time = Acquisition Time + Conversion Time
        // = (31 + 14)*Tad = 45*Tad periods
        // = 45 * 20.5 * Tcy = 922.5*Tcy periods
        //At 7.3728 MIPS, Tcy = 135 ns = Instruction Cycle Time
        //So Tsamp = Tacq + Tconv = 45*Tad(in this example)= 125.1 
microseconds
        //So Fsamp = Sampling Rate ~= 8 KHz
        //All other bits to their default state
        ADCON3bits.SAMC = 31;
        ADCON3bits.ADCS = 40;

        //ADCHS Register
        //When Channel scanning is enabled (ADCON2bits.CSCNA=1)
        //AND Alternate mux sampling is disabled (ADCON2bits.ALTS=0)
        //then ADCHS is a "don't care"
        ADCHS = 0x0000;

        //ADCSSL Register
        //Scan channels AN2, AN3 fas part of scanning sequence
        ADCSSL = 0x000C;

        //ADPCFG Register
        //Set up channels AN2, AN3 as analog inputs and leave rest as 
digital
        //Recall that we configured all A/D pins as digital when code 
execution
        //entered main() out of reset
        ADPCFGbits.PCFG2 = 0;
        ADPCFGbits.PCFG3 = 0;

        //Clear the A/D interrupt flag bit
        IFS0bits.ADIF = 0;

        //Set the A/D interrupt enable bit
        IEC0bits.ADIE = 1;

        //Turn on the A/D converter
        //This is typically done after configuring other registers
        ADCON1bits.ADON = 1;

}

//_ADCInterrupt() is the A/D interrupt service routine (ISR).
//The routine must have global scope in order to be an ISR.
//The ISR name is chosen from the device linker script.
void __attribute__((_interrupt_)) _ADCInterrupt(void)
{
        //Copy the A/D conversion results from ADCBUFn to variables-
        //"Potentiometer" and "TempSensor".
        //Since ADCON2bits.SMPI = 1, only the first two (i.e. SMPI+1) 
ADCBUFn
        //locations are used by the module to store conversion results
        Potentiometer = ADCBUF0;
        TempSensor = ADCBUF1;

        //Clear the A/D Interrupt flag bit or else the CPU will
        //keep vectoring back to the ISR
        IFS0bits.ADIF = 0;

}

meinst du das ?? es gibt auch noch die Funktion delay.s

;Symbol/Literal/Immediate Operand Definitions:
;".equ" directives are similar to "#define" pre-processor directives in 
C
.equ    NANOSEC125,  2          ;125ns approx = 2*TCY at 14.7 MIPS
.equ    MICROSEC, 8*NANOSEC125  ;8*125ns =1us
.equ    MILLISEC, 1000*MICROSEC ;1000*1us = 1000*8*125ns = 1ms

;Global Declarations for Functions and variables:
.global _Delay5us
.global _Delay5ms

;Code Sections:
;Code sections are named ".text"
.section .text


_Delay5us:
;Function Prototype for C:
; void Delay5us(int Count)
; Note: "_" prefixed to the function name is not used when calling the
;       function from a C file.
; Function Execution Time when Count=1 is 5 microseconds at 14.74 MIPS
;
        push    w1              ;Store w1 on to stack
        mov     #MICROSEC, w1   ;w1 = MICROSEC
        dec     w1, w1          ;w1 = w1 - 1
        bra     nz, $-2         ;If w1 != 0 then Branch to previous 
instruction
        dec     w0, w0          ;else w0 = w0 - 1
        bra     nz, $-8         ;If w0 != 0 then Branch back 4 
instructions
        pop     w1              ;else restore w1 from stack
        return                  ;Return to calling routine

_Delay5ms:
;Function Prototype for C:
; void Delay5ms(int Count)
; Note: "_" prefixed to the function name is not used when calling the
;       function from a C file.
; Function Execution Time when Count=1 is 5 milliseconds at 14.74 MIPS
        push    w1              ;Store w1 on to stack
        mov     #MILLISEC, w1   ;w1 = MILLISEC
        dec     w1, w1          ;w1 = w1 - 1
        bra     nz, $-2         ;If w1 != 0 then Branch to previous 
instruction
        dec     w0, w0          ;else w0 = w0 - 1
        bra     nz, $-8         ;If w0 != 0 then Branch back 4 
instructions
        pop     w1              ;else restore w1 from stack
        return                  ;Return to calling routine


.end                            ;End of File

von Laura L. (laura_l)


Lesenswert?

Wooschder schrieb:
> Nachtrag:Wenn du die Hysterese verwendest, benötigst du keinen Tiefpass.

Danke ich werde dann die hysterese googlen...

aber bei meinem TP
unsigned int Tiefpass()
  {
        float i =0;
        float T=0.00001;
        int fg=2000000;
        float q,p,gefilterterwert;
        q=2*3.14*fg*T;
        p=exp(-q);
                while (i<=T)
                 {
                       gefilterterwert=q*Potentiometer + p*potivorher;
                       potivorher=(int)gefilterterwert;
                       i=i+0.0000005;
                 }
    return potivorher;
   }
Stimmt überhaupt dieser tp () ?? die filterordnung habe gar nicht 
berücksichtig und Werte von T und fg habe ich willkürlich 
angenommen...wollte wissen wie ich diese 3 Werte (filterordnung,fg,T) 
richtig bestimmen kann und ob man ein TP überhaupt so programmiert...
Vielen Dank !!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.