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