3 * \author Martin Prudek
4 * \brief Mainfile pro pmsm control.
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*/
21 #include "rpin.h" /*gpclk*/
22 #include "rp_spi.h" /*spi*/
23 #include "misc.h" /*structure for priorities*/
24 #include "pxmc_sin_fixed.h" /*to test sin commutation */
25 #include "pmsm_state.h"
31 /* RL-tool results from first order system approx */
32 /* P=0.16 I=13,4/freq=0.013 */
33 /* Hodnoty upraveny podle skutecnych vysledku */
36 #define PID_P_S 0.16 /*2.3 kmita*/ /*1.4 vhodne jen pro P regulator*/
37 #define PID_I_S 0.0134
43 #define THREAD_SHARED 0
44 #define INIT_VALUE 1 /*init value for semaphor*/
47 #define NSEC_PER_SEC (1000000000) /* The number of nsecs per sec. */
51 struct rpi_state rps={
54 .pwm1=0,.pwm2=0, .pwm3=0,
55 .pwm1=0, .t_pwm2=0, .t_pwm3=0,
57 .duty=0, /* duty cycle of pwm */
58 .index_dist=0, /* distance to index position */
60 .tf_count=0, /*number of transfer*/
61 .desired_pos=0, /* desired position */
67 .log_col_count=0, /* pocet radku zaznamu */
74 * \brief Initilizes GPCLK.
78 initialise(); /*namapovani gpio*/
79 initClock(PLLD_500_MHZ, 10, 0);
80 gpioSetMode(4, FSEL_ALT0);
84 * \brief Terminates GPCLK.
87 inline void clk_disable(){
92 * Count minimum value of three numbers.
93 * Input values must be in range <-2^28;2^28>.
95 int32_t min(int32_t x, int32_t y, int32_t z){
99 sign=(*((uint32_t*)&diff))>>31; /*znamenko -> detekuje, ze y je vetsi*/
100 x=y+sign*diff; /*ulozime mensi cislo, pokud sign>0, pak diff<0 */
103 sign=(*((uint32_t*)&diff))>>31; /*znamenko -> detekuje, ze z je vetsi*/
104 x=z+sign*diff; /*ulozime mensi cislo, pokud sign>0, pak diff<0 */
115 if (rps.log_col_count){
116 for (r=0;r<LOG_ROWS;r++){
130 if (rps.log_col==MAX_LOGS-1){
134 rps.logs[0][rps.log_col]=(int)rps.tf_count;
135 rps.logs[1][rps.log_col]=(int)rps.spi_dat->pozice;
137 rps.logs[2][rps.log_col]=(int)rps.pwm1;
138 rps.logs[3][rps.log_col]=(int)rps.pwm2;
139 rps.logs[4][rps.log_col]=(int)rps.pwm3;
140 rps.logs[5][rps.log_col]=rps.duty;
142 rps.logs[6][rps.log_col]=rps.desired_spd;
143 rps.logs[7][rps.log_col]=rps.speed;
145 rps.logs[8][rps.log_col]=(int)(rps.spi_dat->ch1/rps.spi_dat->adc_m_count);
146 rps.logs[9][rps.log_col]=(int)(rps.spi_dat->ch2/rps.spi_dat->adc_m_count);
147 rps.logs[10][rps.log_col]=(int)(rps.spi_dat->ch0/rps.spi_dat->adc_m_count);
151 if (rps.log_col==rps.log_col_count-1){
152 rps.log_col_count*=2;
153 rps.log_col_count%=MAX_LOGS;
154 for (r=0;r<LOG_ROWS;r++){
155 rps.logs[r]=realloc(rps.logs[r],rps.log_col_count*sizeof(int));
156 if (rps.logs[r]==NULL){
167 * Pripravi psi buffer
169 void prepare_tx(uint8_t * tx){
172 * tx[4] - bity 95 downto 88 - bits that are sent first
173 * tx[5] - bity 87 downto 80
174 * tx[6] - bity 79 downto 72
175 * tx[7] - bity 71 downto 64
176 * tx[8] - bity 63 downto 56
177 * tx[9] - bity 55 downto 48
178 * tx[10] - bity 47 downto 40
179 * tx[11] - bity 39 downto 32
180 * tx[12] - bity 31 downto 24
181 * tx[13] - bity 23 downto 16
182 * tx[14] - bity 15 downto 8
183 * tx[15] - bity 7 downto 0
186 * bit 94 - enable PWM1
187 * bit 93 - enable PWM2
188 * bit 92 - enable PWM3
197 * bits 47 .. 32 - match PWM1
198 * bits 31 .. 16 - match PWM2
199 * bits 15 .. 0 - match PWM3
205 /* keep the 11-bit cap*/
207 if (rps.pwm1>2047) rps.pwm1=2047;
208 if (rps.pwm2>2047) rps.pwm2=2047;
209 if (rps.pwm3>2047) rps.pwm3=2047;
211 tx[0]=rps.test; /*bit 94 - enable PWM1*/
213 /*now we have to switch the bytes due to endianess */
214 /* ARMv6 & ARMv7 instructions are little endian */
216 tx[10]=((uint8_t*)&rps.pwm1)[1]; /*MSB*/
217 tx[11]=((uint8_t*)&rps.pwm1)[0]; /*LSB*/
220 tx[12]=((uint8_t*)&rps.pwm2)[1]; /*MSB*/
221 tx[13]=((uint8_t*)&rps.pwm2)[0]; /*LSB*/
224 tx[14]=((uint8_t*)&rps.pwm3)[1]; /*MSB*/
225 tx[15]=((uint8_t*)&rps.pwm3)[0]; /*LSB*/
232 * \brief Signal handler pro Ctrl+C
236 sem_wait(&rps.thd_par_sem);
238 memset(tx,0,16*sizeof(int));
242 prepare_tx(tx); /*save the data to send*/
248 /*muzeme zavrit semafor*/
249 sem_destroy(&rps.thd_par_sem);
250 printf("\nprogram bezpecne ukoncen\n");
253 void substractOffset(struct rpi_in* data, struct rpi_in* offset){
254 data->pozice=data->pozice_raw-offset->pozice_raw;
259 * Transformace pro uhel pocitany po smeru hodinovych rucicek
261 void dq2alphabeta(int32_t *alpha, int32_t *beta, int d, int q, int32_t sin, int32_t cos){
266 void alphabeta2pwm3(int32_t * ia, int32_t * ib, int32_t *ic,int32_t alpha, int32_t beta){
268 *ib=-alpha/2+beta*887/1024;
269 *ic=-alpha/2-beta*887/1024;
273 * Preocita napeti na jednotlivych civkach na napeti,
274 * ktera budou privedena na svorky motoru.
275 * Tedy na A(yel)-pwm1, B(red)-pwm2, C(blk)-pwm3
277 void transDelta(int32_t * u1, int32_t * u2, int32_t *u3, int32_t ub , int32_t uc){
280 /*vypocte napeti tak, aby odpovidaly rozdily*/
285 /*najde zaporne napeti*/
288 /*dorovna zaporna napeti na nulu*/
293 void inv_trans_comm(int duty){
297 int32_t pwma,pwmb,pwmc;
299 /*melo by byt urceno co nejpresneji, aby faze 'a' splyvala s osou 'alpha'*/
301 /*use it as cyclic 32-bit logic*/
303 pxmc_sincos_fixed_inline(&sin, &cos, pos, 16);
304 dq2alphabeta(&alpha, &beta,0,duty, sin, cos);
307 alphabeta2pwm3(&pwma,&pwmb, &pwmc,alpha,beta);
314 rps.t_pwm1=(uint16_t)pwma;
315 rps.t_pwm3=(uint16_t)pwmb;
316 rps.t_pwm2=(uint16_t)pwmc;
319 void inv_trans_comm_2(int duty){
328 pos+=rps.alpha_offset; /*zarovnani faze 'a' s osou 'alpha'*/
330 /*pro výpočet sin a cos je pouzita 32-bit cyklicka logika*/
332 pxmc_sincos_fixed_inline(&sin, &cos, pos, 16);
334 dq2alphabeta(&alpha, &beta,0,duty, sin, cos);
338 alphabeta2pwm3(&ia,&ib, &ic,alpha,beta);
344 transDelta(&u1,&u2, &u3,ub,uc);
346 rps.pwm1=(uint16_t)u1;
347 rps.pwm2=(uint16_t)u2;
348 rps.pwm3=(uint16_t)u3;
352 * Funkce pravidelne vypisuje posledni zjistenou pozici lokalniho motoru
354 void * pos_monitor(void* param){
357 usleep(1000000); /*1 Hz*/
363 * Multiplication of 11 bit
364 * Zaporne vysledky prvede na nulu.
366 inline uint16_t mult_cap(int32_t s,int d){
370 /* multiplicate as if maximum sinus value was unity */
371 res+=(!(s & 0x10000000))*(((1 << j) & s)>>j)*(d>>(10-j));
376 int sin_commutator(int duty){
377 #define DEGREE_60 715827883
378 #define DEGREE_120 1431655765
379 #define DEGREE_180 2147483648
380 #define DEGREE_240 2863311531
381 #define DEGREE_300 3579139413
386 /*aby prictene uhly mohla byt kulata cisla, musime index posunout*/
388 /*use it as cyclic 32-bit logic*/
390 if (duty>=0){ /*clockwise rotation*/
392 sin = pxmc_sin_fixed_inline(pos+DEGREE_240,10); /*10+1 bity*/ /*-120*/
395 rps.pwm1=(uint16_t)pwm;
398 sin = pxmc_sin_fixed_inline(pos+DEGREE_120,10); /*10+1 bity*/ /*-240*/
401 rps.pwm2=(uint16_t)pwm;
404 sin = pxmc_sin_fixed_inline(pos,10); /*10+1 bity*/
407 rps.pwm3=(uint16_t)pwm;
412 sin = pxmc_sin_fixed_inline(pos+DEGREE_60,10); /*10+1 bity*/ /*-300*/
415 rps.pwm1=(uint16_t)pwm;
418 sin = pxmc_sin_fixed_inline(pos+DEGREE_300,10); /*10+1 bity*/ /*-60-*/
421 rps.pwm2=(uint16_t)pwm;
424 sin = pxmc_sin_fixed_inline(pos+DEGREE_180,10); /*10+1 bity*/ /*-180*/
427 rps.pwm3=(uint16_t)pwm;
433 * Test function to be placed in controll loop.
434 * Switches PWM's at point where they produce same force.
435 * This points are found thanks to IRC position,
438 void simple_ind_dist_commutator(int duty){
439 if (duty>=0){ /* clockwise - so that position increase */
441 if ((rps.index_dist>=45 && rps.index_dist<=373) ||
442 (rps.index_dist>=1048 && rps.index_dist<=1377)){
447 }else if ((rps.index_dist>=373 && rps.index_dist<=711) ||
448 (rps.index_dist>=1377 && rps.index_dist<=1711)){
453 }else if ((rps.index_dist>=0 && rps.index_dist<=45) ||
454 (rps.index_dist>=711 && rps.index_dist<=1048) ||
455 (rps.index_dist>=1711 && rps.index_dist<=1999)){
460 }else{ /*counter-clockwise - position decrease */
462 if ((rps.index_dist>=544 && rps.index_dist<=881) ||
463 (rps.index_dist>=1544 && rps.index_dist<=1878)){
468 }else if ((rps.index_dist>=0 && rps.index_dist<=211) ||
469 (rps.index_dist>=881 && rps.index_dist<=1210) ||
470 (rps.index_dist>=1878 && rps.index_dist<=1999)){
475 }else if ((rps.index_dist>=211 && rps.index_dist<=544) ||
476 (rps.index_dist>=1210 && rps.index_dist<=1544)){
485 * Test function to be placed in controll loop.
486 * Switches PWM's at point where they produce same force
488 inline void simple_hall_commutator(int duty){
489 if (duty>=0){ /* clockwise - so that position increase */
491 if (data.hal2 && !data.hal3){
496 }else if (data.hal1 && !data.hal2){
501 }else if (!data.hal1 && data.hal3){
506 }else{ /*counter-clockwise - position decrease */
508 if (!data.hal2 && data.hal3){
513 }else if (!data.hal1 && data.hal2){
518 }else if (data.hal1 && !data.hal3){
527 * Computation of distance to index.
529 * K dispozici je 12-bit index, to umoznuje ulozit 4096 ruznych bodu
530 * Je nutne vyjadrit 1999 bodu proti i posmeru h.r. od indexu -
532 * =>12 bitu je dostacujicich, pokud nikdy nedojde ke ztrate
536 uint16_t pos = 0x0FFF & data.pozice_raw;
538 uint16_t index = data.index_position;
540 if (index<1999){ /*index e<0,1998> */
541 if (pos<index){ /*pozice e<0,index-1> */
542 /*proti smeru h.r. od indexu*/
544 }else if (pos<=index+1999){ /*pozice e<index,index+1999> */
545 /*po smeru h.r. od indexu*/
547 }else if (pos<index+2096){ /*pozice e<index+2000,index+2095> */
549 }else{ /*pozice e<index+2096,4095> */
550 /*proti smeru h.r. od indexu - podtecena pozice*/
553 }else if (index<=2096){ /*index e<1999,2096>*/
554 if (pos<index-1999){ /*pozice e<0,index-2000> */
556 }else if (pos<index){ /*pozice e<index-1999,index-1> */
557 /*proti smeru h.r. od indexu*/
559 }else if (pos<=index+1999){ /*pozice e<index,index+1999> */
560 /*po smeru h.r. od indexu*/
562 }else { /*pozice e<index+2000,4095> */
565 }else{ /*index e<2097,4095> */
566 if (pos<=index-2097){ /*pozice e<0,index-2097> */
567 /*po smeru h.r. od indexu - pretecena pozice*/
569 }else if (pos<index-1999){ /*pozice e<index-2096,index-2000> */
571 }else if (pos<index){ /*pozice e<index-1999,index-1> */
572 /*proti smeru h.r. od indexu*/
574 }else{ /*pozice e<index,4095> */
575 /*po smeru h.r. od indexu*/
580 rps.index_dist = dist;
589 * Very simple PID regulator.
590 * Now only with P-part so that the error doesnt go to zero.
591 * TODO: add anti-wind up and I and D parts
593 inline void pos_pid(){
595 duty_tmp = PID_P*(rps.desired_pos - (int32_t)data.pozice);
596 if (duty_tmp>MAX_DUTY){
598 }else if (duty_tmp<-MAX_DUTY){
606 * Very simple PID regulator.
607 * Now only with P-part so that the error doesnt go to zero.
610 inline void spd_pid(){
613 error=rps.desired_spd - rps.speed;
614 rps.spd_err_sum+=error;
615 duty_tmp = PID_P_S*error+PID_I_S*rps.spd_err_sum;
616 if (duty_tmp>MAX_DUTY){
618 }else if (duty_tmp<-MAX_DUTY){
631 spd=rps.spi_dat->pozice-rps.old_pos[rps.tf_count%OLD_POS_NUM];
632 rps.speed=(int32_t)spd;
638 * TODO: replace bunch of 'IFs' with Object-like pattern
640 void * read_data(void* param){
642 struct rpi_in pocatek;
644 int interval = 1000000; /* 1ms ~ 1kHz*/
645 uint8_t tx[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ;
647 uint16_t last_index; /*we have index up-to date*/
648 pocatek = spi_read(tx);
649 clock_gettime(CLOCK_MONOTONIC ,&t);
650 /* start after one second */
653 /* wait until next shot */
654 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t, NULL);
655 sem_wait(&rps.thd_par_sem); /*---take semaphore---*/
658 rps.old_pos[rps.tf_count%OLD_POS_NUM]=rps.spi_dat->pozice;
659 prepare_tx(tx); /*save the data to send*/
660 data = spi_read(tx); /*exchange data*/
661 /*subtract initiate postion */
663 substractOffset(&data,&pocatek);
664 compSpeed(); /*spocita rychlost*/
668 last_index=data.index_position;
670 }else if (last_index!=data.index_position){
672 comIndDist(); /*vypocet vzdalenosti indexu*/
674 }else{ /*index je v poradku*/
675 comIndDist(); /*vypocet vzdalenosti indexu*/
678 /* pocitame sirku plneni podle potreb rizeni*/
679 if (rps.pos_reg_ena){ /*pozicni rizeni*/
681 }else if(rps.spd_reg_ena){ /*rizeni na rychlost*/
685 /* sirka plneni prepoctena na jednotlive pwm */
686 if (rps.index_ok && rps.commutate){
687 /*simple_ind_dist_commutator(rps.duty);*/
688 /*sin_commutator(rps.duty);*/
689 inv_trans_comm(rps.duty);
690 inv_trans_comm_2(rps.duty);
691 }else if(!rps.index_ok && rps.commutate){
692 simple_hall_commutator(rps.duty);
695 /*zalogujeme hodnoty*/
696 if (rps.doLogs && !(rps.tf_count%LOG_PERIOD)){
700 sem_post(&rps.thd_par_sem); /*--post semaphore---*/
702 /* calculate next shot */
703 t.tv_nsec += interval;
705 while (t.tv_nsec >= NSEC_PER_SEC) {
706 t.tv_nsec -= NSEC_PER_SEC;
715 * \brief Main function.
719 pthread_t base_thread_id;
720 clk_init(); /* inicializace gpio hodin */
721 spi_init(); /* iniicializace spi*/
723 /*semafor pro detekci zpracovani parametru vlaken*/
724 sem_init(&rps.thd_par_sem,THREAD_SHARED,INIT_VALUE);
727 base_thread_id=pthread_self();
729 /*main control loop*/
730 create_rt_task(&base_thread_id,PRIOR_HIGH,read_data,NULL);
732 /*monitor of current state*/
733 create_rt_task(&base_thread_id,PRIOR_LOW,pos_monitor,NULL);
735 /*wait for commands*/