]> rtime.felk.cvut.cz Git - fpga/rpi-motor-control.git/blob - pmsm-control/test_sw/main_pmsm.c
4ae245b099f497af63c331fea9c32431b20852bb
[fpga/rpi-motor-control.git] / pmsm-control / test_sw / main_pmsm.c
1 /**
2  * \file main_pmsm.c
3  * \author Martin Prudek
4  * \brief Mainfile pro pmsm control.
5  */
6
7 #ifndef NULL
8 #define NULL (void*) 0
9 #endif /*NULL*/
10
11
12 #include <stdlib.h>     /*exit*/
13 #include <signal.h>     /*signal handler Ctrl+C*/
14 #include <stdio.h>      /*printf*/
15 #include <sched.h>      /*sheduler*/
16 #include <unistd.h>     /*usleep*/
17 #include <pthread.h>    /*threads*/
18 #include <time.h>       /*nanosleep*/
19
20 #include "rpin.h"       /*gpclk*/
21 #include "rp_spi.h"     /*spi*/
22 #include "misc.h"       /*structure for priorities*/
23 #include "pxmc_sin_fixed.h"     /*to test sin commutation */
24 #include "pmsm_state.h"
25 #include "cmd_proc.h"
26
27 #define MAX_DUTY        170
28 #define PID_P           0.3
29
30 #define PRIOR_KERN      50
31 #define PRIOR_HIGH      49
32 #define PRIOR_LOW       20
33
34 #define THREAD_SHARED   0
35 #define INIT_VALUE      1       /*init value for semaphor*/
36
37
38 #define NSEC_PER_SEC    (1000000000) /* The number of nsecs per sec. */
39
40
41 struct rpi_in data;
42 struct rpi_state rps={
43         .spi_dat=&data,
44         .test=0,
45         .pwm1=0,.pwm2=0, .pwm3=0,
46         .pwm1=0, .t_pwm2=0, .t_pwm3=0,
47         .commutate=0,
48         .duty=0,                        /* duty cycle of pwm */
49         .index_dist=0,          /* distance to index position */
50         .index_ok=0,
51         .tf_count=0,            /*number of transfer*/
52         .desired_pos=0,         /* desired position */
53         .pos_reg_ena=0,
54         .desired_spd=0,
55         .spd_reg_ena=0
56 };
57
58 /**
59  * \brief Initilizes GPCLK.
60  */
61 int clk_init()
62 {
63         initialise(); /*namapovani gpio*/
64         initClock(PLLD_500_MHZ, 10, 0);
65         gpioSetMode(4, FSEL_ALT0);
66         return 0;
67 }
68 /*
69  * \brief Terminates GPCLK.
70  */
71
72 inline void clk_disable(){
73         termClock(0);
74 }
75 /*
76  * \brief
77  * Count minimum value of three numbers.
78  * Input values must be in range <-2^28;2^28>.
79  */
80 int32_t min(int32_t x, int32_t y, int32_t z){
81         int32_t diff,sign;
82
83         diff=x-y; /*rozdil*/
84         sign=(*((uint32_t*)&diff))>>31; /*znamenko -> detekuje, ze y je vetsi*/
85         x=y+sign*diff; /*ulozime mensi cislo, pokud sign>0, pak diff<0 */
86
87         diff=x-z; /*rozdil*/
88         sign=(*((uint32_t*)&diff))>>31; /*znamenko -> detekuje, ze z je vetsi*/
89         x=z+sign*diff; /*ulozime mensi cislo, pokud sign>0, pak diff<0 */
90
91         return x;
92 }
93
94 /**
95  * \brief Signal handler pro Ctrl+C
96  */
97 void appl_stop(){
98         spi_disable();
99         clk_disable();
100         /*muzeme zavrit semafor*/
101         sem_destroy(&rps.thd_par_sem);
102         printf("\nprogram bezpecne ukoncen\n");
103 }
104
105 void substractOffset(struct rpi_in* data, struct rpi_in* offset){
106         data->pozice_raw=data->pozice;
107         data->pozice-=offset->pozice;
108         return;
109 }
110 /*
111  * \brief
112  * Transformace pro uhel pocitany po smeru hodinovych rucicek
113  */
114 void dq2alphabeta(int32_t *alpha, int32_t *beta, int d, int q, int32_t sin, int32_t cos){
115         *alpha=cos*d+sin*q;
116         *beta=-sin*d+cos*q;
117         return;
118 }
119 void alphabeta2pwm3(int32_t * ia, int32_t * ib, int32_t *ic,int32_t alpha, int32_t beta){
120         *ia=alpha;
121         *ib=-alpha/2+beta*887/1024;
122         *ic=-alpha/2-beta*887/1024;
123 }
124 /*
125  * \brief
126  * Preocita napeti na jednotlivych civkach na napeti,
127  *      ktera budou privedena na svorky motoru.
128  *      Tedy na A(yel)-pwm1, B(red)-pwm2, C(blk)-pwm3
129  */
130 void transDelta(int32_t * u1, int32_t * u2, int32_t *u3, int32_t ub , int32_t uc){
131         int32_t t;
132
133         /*vypocte napeti tak, aby odpovidaly rozdily*/
134         *u1=uc;
135         *u2=uc+ub;
136         *u3=0;
137
138         /*najde zaporne napeti*/
139         t=min(*u1,*u2,*u3);
140
141         /*dorovna zaporna napeti na nulu*/
142         *u1-=t;
143         *u2-=t;
144         *u3-=t;
145 }
146 void inv_trans_comm(int duty){
147         uint32_t pos;
148         int32_t sin, cos;
149         int32_t alpha, beta;
150         int32_t pwma,pwmb,pwmc;
151         pos=rps.index_dist;
152         /*melo by byt urceno co nejpresneji, aby faze 'a' splyvala s osou 'alpha'*/
153         pos+=717;
154         /*use it as cyclic 32-bit logic*/
155         pos*=4294967;
156         pxmc_sincos_fixed_inline(&sin, &cos, pos, 16);
157         dq2alphabeta(&alpha, &beta,0,duty, sin, cos);
158         alpha>>=16;
159         beta>>=16;
160         alphabeta2pwm3(&pwma,&pwmb, &pwmc,alpha,beta);
161
162         if (pwma<0) pwma=0;
163         if (pwmb<0) pwmb=0;
164         if (pwmc<0) pwmc=0;
165
166
167         rps.t_pwm1=(uint16_t)pwma;
168         rps.t_pwm3=(uint16_t)pwmb;
169         rps.t_pwm2=(uint16_t)pwmc;
170 }
171
172 void inv_trans_comm_2(int duty){
173         uint32_t pos;
174         int32_t sin, cos;
175         int32_t alpha, beta;
176         int32_t ua,ub,uc;
177         int32_t ia,ib,ic;
178         int32_t u1,u2,u3;
179         pos=rps.index_dist;
180
181         pos+=960; /*zarovnani faze 'a' s osou 'alpha'*/
182
183         /*pro výpočet sin a cos je pouzita 32-bit cyklicka logika*/
184         pos*=4294967;
185         pxmc_sincos_fixed_inline(&sin, &cos, pos, 16);
186
187         dq2alphabeta(&alpha, &beta,0,duty, sin, cos);
188         alpha>>=16;
189         beta>>=16;
190
191         alphabeta2pwm3(&ia,&ib, &ic,alpha,beta);
192
193         ua=ia;
194         ub=ib;
195         uc=ic;
196
197         transDelta(&u1,&u2, &u3,ub,uc);
198
199         rps.pwm1=(uint16_t)u1;
200         rps.pwm2=(uint16_t)u2;
201         rps.pwm3=(uint16_t)u3;
202 }
203 void prepare_tx(uint8_t * tx){
204
205         /*Data format:
206          * tx[4] - bity 95 downto 88 - bits that are sent first
207          * tx[5] - bity 87 downto 80
208          * tx[6] - bity 79 downto 72
209          * tx[7] - bity 71 downto 64
210          * tx[8] - bity 63 downto 56
211          * tx[9] - bity 55 downto 48
212          * tx[10] - bity 47 downto 40
213          * tx[11] - bity 39 downto 32
214          * tx[12] - bity 31 downto 24
215          * tx[13] - bity 23 downto 16
216          * tx[14] - bity 15 downto 8
217          * tx[15] - bity 7 downto 0
218          *
219          * bit 95 - ADC reset
220          * bit 94 - enable PWM1
221          * bit 93 - enable PWM2
222          * bit 92 - enable PWM3
223          * bit 91 - shutdown1
224          * bit 90 - shutdown2
225          * bit 89 - shutdown3
226          *      .
227          *      .
228          *      Unused
229          *      .
230          *      .
231          * bits 47 .. 32 - match PWM1
232          * bits 31 .. 16 - match PWM2
233          * bits 15 .. 0  - match PWM3
234          */
235
236
237         uint16_t tmp;
238
239         /* keep the 11-bit cap*/
240
241         if (rps.pwm1>2047) rps.pwm1=2047;
242         if (rps.pwm2>2047) rps.pwm2=2047;
243         if (rps.pwm3>2047) rps.pwm3=2047;
244
245         tx[0]=rps.test; /*bit 94 - enable PWM1*/
246
247         /*now we have to switch the bytes due to endianess */
248         /* ARMv6 & ARMv7 instructions are little endian */
249         /*pwm1*/
250         tx[10]=((uint8_t*)&rps.pwm1)[1]; /*MSB*/
251         tx[11]=((uint8_t*)&rps.pwm1)[0]; /*LSB*/
252
253         /*pwm2*/
254         tx[12]=((uint8_t*)&rps.pwm2)[1]; /*MSB*/
255         tx[13]=((uint8_t*)&rps.pwm2)[0]; /*LSB*/
256
257         /*pwm3*/
258         tx[14]=((uint8_t*)&rps.pwm3)[1]; /*MSB*/
259         tx[15]=((uint8_t*)&rps.pwm3)[0]; /*LSB*/
260
261
262 }
263 /**
264  * Funkce pravidelne vypisuje posledni zjistenou pozici lokalniho motoru
265  */
266 void * pos_monitor(void* param){
267         while(1){
268                 printData(&rps);
269                 usleep(1000000);        /*1 Hz*/
270         }
271         return (void*)0;
272 }
273 /*
274  * \brief
275  * Multiplication of 11 bit
276  * Zaporne vysledky prvede na nulu.
277  */
278 inline uint16_t mult_cap(int32_t s,int d){
279         int j;
280         int res=0;
281         for(j=0;j!=11;j++){
282                 /* multiplicate as if maximum sinus value was unity */
283                 res+=(!(s & 0x10000000))*(((1 << j) & s)>>j)*(d>>(10-j));
284         }
285         return res;
286 }
287 inline
288 int sin_commutator(int duty){
289         #define DEGREE_60        715827883
290         #define DEGREE_120      1431655765
291         #define DEGREE_180      2147483648
292         #define DEGREE_240      2863311531
293         #define DEGREE_300      3579139413
294         uint32_t j,pos;
295         int32_t sin;
296         pos=rps.index_dist;
297         int32_t pwm;
298         /*aby prictene uhly mohla byt kulata cisla, musime index posunout*/
299         pos+=38;
300         /*use it as cyclic 32-bit logic*/
301         pos*=4294967;
302         if (duty>=0){   /*clockwise rotation*/
303                 /* 1st phase */
304                 sin = pxmc_sin_fixed_inline(pos+DEGREE_240,10); /*10+1 bity*/ /*-120*/
305                 pwm=sin*duty/1024;
306                 if (pwm<0) pwm=0;
307                 rps.pwm1=(uint16_t)pwm;
308
309                 /* 2nd phase */
310                 sin = pxmc_sin_fixed_inline(pos+DEGREE_120,10); /*10+1 bity*/ /*-240*/
311                 pwm=sin*duty/1024;
312                 if (pwm<0) pwm=0;
313                 rps.pwm2=(uint16_t)pwm;
314
315                 /* 3rd phase */
316                 sin = pxmc_sin_fixed_inline(pos,10); /*10+1 bity*/
317                 pwm=sin*duty/1024;
318                 if (pwm<0) pwm=0;
319                 rps.pwm3=(uint16_t)pwm;
320         }else{
321                 duty=-duty;
322
323                 /* 1st phase */
324                 sin = pxmc_sin_fixed_inline(pos+DEGREE_60,10); /*10+1 bity*/ /*-300*/
325                 pwm=sin*duty/1024;
326                 if (pwm<0) pwm=0;
327                 rps.pwm1=(uint16_t)pwm;
328
329                 /* 2nd phase */
330                 sin = pxmc_sin_fixed_inline(pos+DEGREE_300,10); /*10+1 bity*/ /*-60-*/
331                 pwm=sin*duty/1024;
332                 if (pwm<0) pwm=0;
333                 rps.pwm2=(uint16_t)pwm;
334
335                 /* 3rd phase */
336                 sin = pxmc_sin_fixed_inline(pos+DEGREE_180,10); /*10+1 bity*/ /*-180*/
337                 pwm=sin*duty/1024;
338                 if (pwm<0) pwm=0;
339                 rps.pwm3=(uint16_t)pwm;
340         }
341         return 0;
342 }
343 /*
344  * \brief
345  * Test function to be placed in controll loop.
346  * Switches PWM's at point where they produce same force.
347  * This points are found thanks to IRC position,
348  */
349 inline
350 void simple_ind_dist_commutator(int duty){
351         if (duty>=0){ /* clockwise - so that position increase */
352                 /* pwm3 */
353                 if ((rps.index_dist>=45 && rps.index_dist<=373) ||
354                 (rps.index_dist>=1048 && rps.index_dist<=1377)){
355                         rps.pwm1=0;
356                         rps.pwm2=0;
357                         rps.pwm3=duty;
358                         /* pwm1 */
359                 }else if ((rps.index_dist>=373 && rps.index_dist<=711) ||
360                 (rps.index_dist>=1377 && rps.index_dist<=1711)){
361                         rps.pwm1=duty;
362                         rps.pwm2=0;
363                         rps.pwm3=0;
364                         /* pwm2 */
365                 }else if ((rps.index_dist>=0 && rps.index_dist<=45) ||
366                 (rps.index_dist>=711 && rps.index_dist<=1048) ||
367                 (rps.index_dist>=1711 && rps.index_dist<=1999)){
368                         rps.pwm1=0;
369                         rps.pwm2=duty;
370                         rps.pwm3=0;
371                 }
372         }else{  /*counter-clockwise - position decrease */
373                 /* pwm3 */
374                 if ((rps.index_dist>=544 && rps.index_dist<=881) ||
375                 (rps.index_dist>=1544 && rps.index_dist<=1878)){
376                         rps.pwm1=0;
377                         rps.pwm2=0;
378                         rps.pwm3=-duty;
379                         /* pwm1 */
380                 }else if ((rps.index_dist>=0 && rps.index_dist<=211) ||
381                 (rps.index_dist>=881 && rps.index_dist<=1210) ||
382                 (rps.index_dist>=1878 && rps.index_dist<=1999)){
383                         rps.pwm1=-duty;
384                         rps.pwm2=0;
385                         rps.pwm3=0;
386                         /* pwm2 */
387                 }else if ((rps.index_dist>=211 && rps.index_dist<=544) ||
388                 (rps.index_dist>=1210 && rps.index_dist<=1544)){
389                         rps.pwm1=0;
390                         rps.pwm2=-duty;
391                         rps.pwm3=0;
392                 }
393         }
394 }
395 /*
396  * \brief
397  * Test function to be placed in controll loop.
398  * Switches PWM's at point where they produce same force
399  */
400 inline void simple_hall_commutator(int duty){
401         if (duty>=0){ /* clockwise - so that position increase */
402                 /* pwm3 */
403                 if (data.hal2 && !data.hal3){
404                         rps.pwm1=0;
405                         rps.pwm2=0;
406                         rps.pwm3=duty;
407                         /* pwm1 */
408                 }else if (data.hal1 && !data.hal2){
409                         rps.pwm1=duty;
410                         rps.pwm2=0;
411                         rps.pwm3=0;
412                         /* pwm2 */
413                 }else if (!data.hal1 && data.hal3){
414                         rps.pwm1=0;
415                         rps.pwm2=duty;
416                         rps.pwm3=0;
417                 }
418         }else{  /*counter-clockwise - position decrease */
419                 /* pwm3 */
420                 if (!data.hal2 && data.hal3){
421                         rps.pwm1=0;
422                         rps.pwm2=0;
423                         rps.pwm3=-duty;
424                         /* pwm1 */
425                 }else if (!data.hal1 && data.hal2){
426                         rps.pwm1=-duty;
427                         rps.pwm2=0;
428                         rps.pwm3=0;
429                         /* pwm2 */
430                 }else if (data.hal1 && !data.hal3){
431                         rps.pwm1=0;
432                         rps.pwm2=-duty;
433                         rps.pwm3=0;
434                 }
435         }
436 }
437 /**
438  * \brief
439  * Computation of distance to index.
440  *
441  * K dispozici je 12-bit index, to umoznuje ulozit 4096 ruznych bodu
442  * Je nutne vyjadrit 1999 bodu proti i posmeru h.r. od indexu -
443  *      to je 3999 bodu
444  *      =>12 bitu je dostacujicich, pokud nikdy nedojde ke ztrate
445  *              signalu indexu
446  */
447 void comIndDist(){
448         uint16_t pos = 0x0FFF & data.pozice_raw;
449         uint16_t dist;
450         uint16_t index = data.index_position;
451
452         if (index<1999){                /*index e<0,1998> */
453                 if (pos<index){                 /*pozice e<0,index-1> */
454                         /*proti smeru h.r. od indexu*/
455                         dist=pos+2000-index;
456                 }else if (pos<=index+1999){     /*pozice e<index,index+1999> */
457                         /*po smeru h.r. od indexu*/
458                         dist=pos-index;
459                 }else if (pos<index+2096){      /*pozice e<index+2000,index+2095> */
460                         goto index_lost;
461                 }else{                          /*pozice e<index+2096,4095> */
462                         /*proti smeru h.r. od indexu - podtecena pozice*/
463                         dist=pos-index-2096;
464                 }
465         }else if (index<=2096){         /*index e<1999,2096>*/
466                 if (pos<index-1999){            /*pozice e<0,index-2000> */
467                         goto index_lost;
468                 }else if (pos<index){           /*pozice e<index-1999,index-1> */
469                         /*proti smeru h.r. od indexu*/
470                         dist=pos+2000-index;
471                 }else if (pos<=index+1999){     /*pozice e<index,index+1999> */
472                         /*po smeru h.r. od indexu*/
473                         dist=pos-index;
474                 }else {                         /*pozice e<index+2000,4095> */
475                         goto index_lost;
476                 }
477         }else{                          /*index e<2097,4095> */
478                 if (pos<=index-2097){           /*pozice e<0,index-2097> */
479                         /*po smeru h.r. od indexu - pretecena pozice*/
480                         dist=4096+pos-index;
481                 }else if (pos<index-1999){      /*pozice e<index-2096,index-2000> */
482                         goto index_lost;
483                 }else if (pos<index){           /*pozice e<index-1999,index-1> */
484                         /*proti smeru h.r. od indexu*/
485                         dist=pos+2000-index;
486                 }else{                          /*pozice e<index,4095> */
487                         /*po smeru h.r. od indexu*/
488                         dist=pos-index;
489                 }
490         }
491
492         rps.index_dist = dist;
493         return;
494
495         index_lost:
496                 rps.index_ok=0;
497                 return;
498 }
499 /*
500  * \brief
501  * Very simple PID regulator.
502  * Now only with P-part so that the error doesnt go to zero.
503  * TODO: add anti-wind up and I and D parts
504  */
505 inline void pos_pid(){
506         int duty_tmp;
507         duty_tmp = PID_P*(rps.desired_pos - (int32_t)data.pozice);
508         if (duty_tmp>MAX_DUTY){
509                 rps.duty=MAX_DUTY;
510         }else if (duty_tmp<-MAX_DUTY){
511                 rps.duty=-MAX_DUTY;
512         }else{
513                 rps.duty = duty_tmp;
514         }
515 }
516 /*
517  * \brief
518  * Feedback loop.
519  * TODO: replace bunch of 'IFs' with Object-like pattern
520  */
521 void * read_data(void* param){
522         int i;
523         struct rpi_in pocatek;
524         struct timespec t;
525         int interval = 1000000; /* 1ms ~ 1kHz*/
526         uint8_t tx[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ;
527         char first=1;
528         uint16_t last_index;                            /*we have index up-to date*/
529         pocatek = spi_read(tx);
530         clock_gettime(CLOCK_MONOTONIC ,&t);
531         /* start after one second */
532         t.tv_sec++;
533                 while(1){
534                         /* wait until next shot */
535                         clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t, NULL);
536                         sem_wait(&rps.thd_par_sem);             /*---take semaphore---*/
537                         prepare_tx(tx);                 /*save the data to send*/
538                         data = spi_read(tx);            /*exchange data*/
539                         /*subtract initiate postion */
540                         rps.tf_count++;
541                         substractOffset(&data,&pocatek);
542
543                         if (!rps.index_ok){
544                                 if (first){
545                                         last_index=data.index_position;
546                                         first=0;
547                                 }else if (last_index!=data.index_position){
548                                         rps.index_ok=1;
549                                         comIndDist();   /*vypocet vzdalenosti indexu*/
550                                 }
551                         }else{ /*index je v poradku*/
552                                 comIndDist();           /*vypocet vzdalenosti indexu*/
553                         }
554                         /* pocitame sirku plneni podle potreb rizeni*/
555                         if (rps.pos_reg_ena){
556                                 pos_pid();
557                         }
558                         /* sirka plneni prepoctena na jednotlive pwm */
559                         if (rps.index_ok && rps.commutate){
560                                 /*simple_ind_dist_commutator(rps.duty);*/
561                                 /*sin_commutator(rps.duty);*/
562                                 inv_trans_comm(rps.duty);
563                                 inv_trans_comm_2(rps.duty);
564                         }else if(!rps.index_ok && rps.commutate){
565                                 simple_hall_commutator(rps.duty);
566                         }
567                         sem_post(&rps.thd_par_sem);             /*--post semaphore---*/
568
569                         /* calculate next shot */
570                         t.tv_nsec += interval;
571
572                         while (t.tv_nsec >= NSEC_PER_SEC) {
573                                 t.tv_nsec -= NSEC_PER_SEC;
574                                 t.tv_sec++;
575                         }
576
577                 }
578 }
579
580
581
582 /**
583  * \brief Main function.
584  */
585
586 int main(){
587         pthread_t base_thread_id;
588         clk_init();             /* inicializace gpio hodin */
589         spi_init();             /* iniicializace spi*/
590
591         /*semafor pro detekci zpracovani parametru vlaken*/
592         sem_init(&rps.thd_par_sem,THREAD_SHARED,INIT_VALUE);
593         setup_environment();
594
595         base_thread_id=pthread_self();
596
597         /*main control loop*/
598         create_rt_task(&base_thread_id,PRIOR_HIGH,read_data,NULL);
599
600         /*monitor of current state*/
601         create_rt_task(&base_thread_id,PRIOR_LOW,pos_monitor,NULL);
602
603         /*wait for commands*/
604         poll_cmd(&rps);
605
606         return 0;
607 }