Dit circuit stuurt een stapmotor aan om iets van positie te laten veranderen.

Bij toepassing als vergrendeling moet je de persoonsveiligheid niet uit het oog verliezen. Denk bijv. wat te doen bij brand of onwel worden. Zijn er genoeg andere vluchtwegen. Kan iemand anders er nog in. Ga er niet vanuit dat deze schakeling altijd zal werken.

Er zijn 2 buttons A en B.
A: 1 x drukken motor zorgt voor gaan naar eindpunt 1 en neemt dan weer een stukje terug.
Nog een keer drukken idem dan voor eindpunt 2. Het stukje terug is om een (opzettelijke grote) speling toe te
laten daardoor kun je met de hand de positie veranderen zonder de motorweerstand en eventuele
tandwielvertragingweerstand te ondervinden via een soort koppelstuk. ( <-> ), ( en ) zitten aan elkaar vast en is de
uiteindelijke positieverplaatsing, <-> motorbeweging, spaties ertussen is de speling.
B: ijken, is om eindpunten 1( en )2 te zetten. 1-----( <-> )----2

Schakeling wordt gevoed uit een beltrafo met als backup 8 x 1,2V NiMh cellen. De backup kan bij weinig gebruik dagen lang mee.

Als stappenmotor driver kun je een TMC2209 of LV8729 nemen maar geen A4988 vanwege beperkte spanningsrange.
De PIC 12F675 genereert step en richting signalen voor de driver. Zelf kan hij in het circuit zelf geprogrammeerd worden via een zgn ISP interface. R8,R11,R4,C4 en D6 zijn daarbij noodzakelijk.

Naast de button signalen gebruiken we ook 2 analoge ingangen om resp. de motorstroom en de ( ) positie te meten.

Motorstroom meten via weerstanden R9 en R13 waar de groundstromen doorheen gaan. D8 voorkomt dat de driver nooit te weinig spanning krijgt. De driver veroorzaakt veel storing die gefilterd/gemiddeld wordt door R12 en C10. GP2 wordt zowel gebruikt om de stroom te meten als ook om de buzzer aan te sturen mocht die een grens overgaan bij bijv. blokade. De PIC bepaald de grens samen met RV1. Door het tweevoudig gebruik zijn D9,D19,R7,R19 en C12 noodzakelijk. De buzzer heeft een controlingang C, buzzed indien hoger dan 3V maar moet wel minimaal 150ms aan staan.

10k potentiometer bij j4 geeft de positie door. In plaats van de potentiometer kan men ook 2 buttonschakelaars bij de eindpunten plaatsen plus een aantal weerstanden.

Om in rust geen motorstroom te laten lopen is het circuit rond Q1 nodig. Zolang de steps komen is de laag actieve enable laag. Echter bij opstarten blijkt er toch motorstroom te lopen bij no steps, de LV8729 krijgt geen resetsignaal. Slechts 1 step is nodig om dit recht te zetten.

Het programma is met MPLAB X 6.20 ontwikkeld, je kan dan nog PICkit3 gebruiken, met C als programmeertaal.
Ik kan MPLAB X niemand aanbevelen. C is leuk als je pointers wil gebruiken of vreemde datatypen.

Omdat deze PIC geen pwm uitgang bezit en ik geen interrupts wou gebruiken doe ik moeilijk met telkens 40 steps. Ik gebruik een motorvertraging van 1/17 zodat beweging niet al te groot is. Na 40 steps wordt dan telkens de motorstroom en de positie gemeten. Indien de motorstroom te hoog is gaat de buzzer af zodat men weet dat er iets speelt.

Met button B callibratie worden de eindpunten gezet. Druk op B tot je bij een eindpunt bent laat dan los en druk op button A. Doe dit nog een keer om het andere eindpunt te zetten.

Het geheel werkt nog niet helemaal zoals zou moeten. De buzzer gaat vaak de mist in misschien door storing pieken. De motorstroom neemt slechts 10% toe bij veel weerstand. De drivers hebben natuurlijk een ingebouwde stroombegrenzing en er zal ook voor de hoogte van de voedingsspanning gecompenseerd moeten worden. .

Een betere indicatie is positie verandering tgv aantal steps te volgen. Zonder de mechanica kun je dat niet testen.

Gebruikte stapmotor is ACT MOTOR 17HS5425, 0,48 Nm, 1,8mH, 1,25 Ohm, 3V, 2,5A. Er loopt ongeveer 0,8A voedingstroom.

Bij lage voedingspanningen 2,7-10,8V kan de driver DRV8833 gebruikt worden. Deze heeft echter geen indexer aan boord, geen step en direction, maar gelijk brugaansturing. Voor full step kun je een makkelijke routine gebruiken. Zie dan voor timing van de stromen een driver datasheet.

Het 1e scoopplaatje geeft het moment aan wanneer de buzzer aan gaat (geel verspringt). Het groen komt van de tracer 500µs voor en 500µs na referentie pulsen. Daar tussenin kunnen we zien hoe groot ADRESL is nml. 1100µs/18,5µs=59 is ongeveer Ilimit=60. Het is 18,5 en geen 10µs tgv. extra FOR loop vertraging. Dit is uitgerekend door naar de pulslengte van de loper te kijken. Loper was 1,68V/5V *256=86 en pulsduur loper is 1600µs, 1600/86=18,5µs. 2e plaatje is van zonder buzzen tussen 2 x40 steps in (blauw). Je ziet nu 2 treintjes 1e van de stroommeting gevolgd door de lopermeting. Rood is spanning over C10.


/*
 * File:   Deu1.c
 * Author: deluxe
 *
 * Created on July 24, 2026, 11:03 PM
 */


// 'C' source line config statements
// PIC12F675 Configuration Bit Settings


#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-Up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF       // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
//#include <pic12f675.h>
//#include <builtins.h>
//#include <stdint.h>

/*
* GP0  --> "DIR" LV8729 (U3) / "PGD" PICkit (Pin5)
* GP1  --> "Open/Close" Switch (S1,S2) / "PGC" PICkit (Pin4)
* GP2  --> "AN2" Current Sense (R9) / Buzzer
* GP3  --> "Calibration" Switch (S3) / "VPP" PICkit (Pin1)
* GP4  --> "AN3" Potmeter (P1)
* GP5  --> "STEP" LV8729 / 
*/

//int _XTAL_FREQ=4000000;
#define _XTAL_FREQ 4000000  
    
#define Dir       GP0  // 0=openen / 1=sluiten
#define DoCycle   GP1  // open or close / 1 if button pressed
#define Buzzer    GP2  // 1 to buzz
#define Calibrate GP3  // 1 if button pressed
#define Potmeter  GP4  // 
#define Step      GP5  // Step level

char k=0;
char l=0;

void trace(char K , char L) // voor debugging / K aantal perioden / L duur
{
    TRISIO=0b00011100 ;   //GP1 temp output
    GP1=0;
    __delay_us(500);
    for (k=1;k<=K;k++)
       {
        GP1=1;
        for (l=1;l<=L;l++) __delay_us(10);
        GP1=0;
        for (l=1;l<=L;l++) __delay_us(10);
       };
    GP1=1;
    __delay_us(500);
    TRISIO=0b00011110 ;   //GP1 is weer input
}

unsigned int n=0;
const unsigned int period=2500; // in microseconds

void steps(unsigned int N)       // N aantal stappen
{
    //trace(2,10);
    for (n=1;n<=N;n++)
 {
     GP5=1;
     __delay_us(period/2);
     GP5=0;                      // met 0 eindigen zodat driver gedisabled wordt
     __delay_us(period/2);
 }
}


const char Isense =2;
const char Loper =4;
char result =0;

char meet(char bron) // 2=current / 4=loper potmeter
{
  ANSEL=0b01101100;//0 110=4MHz>16us clock 1100 AN3 and AN2 selected as analog
  if (bron==Isense) ADCON0=0b10001000;//resultaat rechts, Vdd=ref, meet AN2
  if (bron==Loper)  ADCON0=0b00001100;//resultaat links, Vdd=ref, meet AN3
  ADON=1; // Enable ADC
  __delay_us(100);                       // setup sample hold time
  GO=1;                                 // converteer
  while (GO_nDONE==1);      // wacht tot conversie klaar is / zie pic12f675.h
  if (bron==Isense){result= ADRESL;trace(1,ADRESL);}//bereik 0 tot 256*5mV/8bit
  if (bron==Loper) {result= ADRESH;trace(1,ADRESH);}//bereik 0 tot VDD=5V/8bit
  ANSEL = 0;
  return result;
}                     //wat ook kan int result = ((ADRESH<<8)+ADRESL);


void buzz(void)
{
    //trace(3,10);
    TRISIO = 0b00011010;  // GP2 tijdelijk output
    Buzzer=1;             //buzzer aan
    __delay_ms(150);      // minimum aan tijd
    Buzzer=0;             //buzzer uit
    TRISIO = 0b00011110;  // GP2 nu input om current te sensen
    __delay_ms(150);      //ingang settle time 10uF 
}


__bit GoOn ; // __bit type kun je niet initializen
             //binnen main, static gebruiken er buiten is het global

__eeprom char LLower=70;      // voor stroomuitval eindpunten behoeden
__eeprom char UUpper=180;     // ongeveer 128 apart = 1/3 omwenteling =20mm

void main(void) 
{
 
//////////////////////////////
// MCU I/O initialization
//////////////////////////////
       
// GPIO = 0b00001010 ;     //
OPTION_REG = 0b11010001; // 1101 0001 tmr0 enabled prescale 4 internal clock = 1MHz
INTCON = 0b00000000;     //0000 0000 no interupts
TRISIO = 0b00011110;     // 0001 1110 only DIR and Step are outputs
                         // during buzz GP2 is temp output
ADCON0 = 0b10001001;     // 1000 10=AN2 11=AN3 0= no start 1=AD is ON
//ANSEL  = 0b01101100 ;    // 0 110=4MHz/64=62,5kHz`16us 1100 AN3 and AN2 selected as analog
ANSEL = 0;
CMCON  = 0b00000111 ;    // zet compare analog interfere uit
  


const unsigned int Turn1Third =17*66;// vertraging x 1/3 omwenteling / van motor tot omwenteling

const char Ilimit=60;    // =60*5V/1024, 0,3V/0,5Ohm=0,6A

const char lowermin=60;
const char lowerplus=100;
const char uppermin=150;
const char upperplus=190;

char result;

char Lower; //ondereindpunt
char Upper; //boveneindpunt

  
steps(1);   // stepper driver resets to off state

Lower=LLower;
Upper=UUpper;

while(1) 
  {
    GoOn=0;
    
    //GP1=1; kunnen GP1 niet setten want het is een input
    //GP3=1;
   
    if (!GP3)       //callibratie Lower, Upper
    {
        //trace(6,10);
        __delay_ms(200);    //anti dender
        while (!GP3)     
        steps(20);  //beweeg voorzichtig naar een eindpunt, stop zelf 
        while (GP1); //wacht op cycle button om eindpunt te zetten
        result=meet(Loper);
        if ((GP0==0)&(result>=lowermin)&(result<=lowerplus)) 
           {Lower=result;LLower=result;};
        if ((GP0==1)&(result>=uppermin)&(result<=upperplus)) 
           {Upper=result;UUpper=result;};
        GP0=!GP0;         //richting verandert om tot andere eindpunt te komen
        __delay_ms(200); // anti dender GP1
    };
    
    
    while (!GP1 | GoOn)   // openen en sluiten
      {
         //trace(5,10);
         GoOn=1;     //beweeg tot cycle afgelopen is
         steps(40);  //beweeg voorzichtig 0,1s = 40 steps
        
         if (meet(Isense)>Ilimit) buzz(); //alarm bij teveel weerstand
        
         // kom je bij een eindpunt keerom en beweeg 1/3 omwenteling terug
         result=meet(Loper);
         if (result<Lower|result>Upper) {GP0=!GP0;steps(Turn1Third);GoOn=0;};//Turn1Third
      };
  }
} 

Motorstroom meten via weerstanden R9 en R13 waar de groundstromen doorheen gaan. D8 voorkomt dat de driver nooit te weinig spanning krijgt. De driver veroorzaakt veel storing die gefilterd/gemiddeld wordt door R12 en C10. GP2 wordt zowel gebruikt om de stroom te meten als ook om de buzzer aan te sturen mocht die een grens overgaan bij bijv. blokade. De PIC bepaald de grens samen met RV1. Door het tweevoudig gebruik zijn D9,D19,R7,R19 en C12 noodzakelijk.

Waarom ? De TMC2209 met StallGuard4 doet dat zelf. Met je PIC hoef je alleen maar DIAG te monitoren.