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