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