Hi guys, I'd like to know if it's possible to change the period of a periodic task running rt_make_periodic (or pthread_make_periodic_np) more than one time in the same module. I mean: each time my linux program write to a fifo a handler runs in my rt-module with rt_make_periodic (or pthread_make_periodic_np) using period values passed through the fifo (something like frank_app running START_TASK many times, each time with a "msg.period" different) My second question is about the reasons why i can't write to a fifo. I create the fifos in my rt-module (with rtf_create), but when i run a monitor program and try write to the fifo the return value is minor than 0 and i can't reinitialize my periodic task. thanks in advance, Daniela. P.S.: I'm using the code in attachment. Sorry, it's really disordered and ugly and i hope that someone can urderstand it ?:) Thanks again, Dani.
#include <stdio.h> #include <errno.h> #include <sys/time.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <rtl_fifo.h> #include <rtl_time.h> #include "declaracoes.h" #define BUFSIZE 70 char buf[BUFSIZE]; int main() { fd_set rfds; struct timeval tv; int retval; int fd0, fd1, ctl; int n; int i; mensagens MSG; if ((fd0 = open("/dev/rtf0", O_RDONLY)) < 0) { fprintf(stderr, "Error opening /dev/rtf0\n"); exit(1); } if ((fd1 = open("/dev/rtf3", O_RDONLY)) < 0) { fprintf(stderr, "Error opening /dev/rtf3\n"); exit(1); } if ((ctl = open("/dev/rtf4", O_WRONLY)) < 0) { fprintf(stderr, "Error opening /dev/rtf4\n"); exit(1); } /* now start the tasks */ MSG.command = START_TASK; MSG.tarefa = 0; MSG.period = 200000; if (write(ctl, &MSG, sizeof(MSG)) < 0) { fprintf(stderr, "Can't send a command to START RT-task0\n"); exit(1); } MSG.tarefa = 1; MSG.period = 200000; if (write(ctl, &MSG, sizeof(MSG)) < 0) { fprintf(stderr, "Can't send a command to START RT-task1\n"); exit(1); } for (i = 0; i < 200; i++) { /* FD_ZERO(&rfds); FD_SET(fd0, &rfds); FD_SET(fd1, &rfds); tv.tv_sec = 1; tv.tv_usec = 0; retval = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); if (retval > 0) { if (FD_ISSET(fd0, &rfds)) { n = read(fd0, buf, BUFSIZE - 1); buf[n] = 0; printf("FIFO 1: %s\n", buf); } if (FD_ISSET(fd1, &rfds)) { n = read(fd1, buf, BUFSIZE - 1); buf[n] = 0; printf("FIFO 2: %s\n", buf); } } */ } fprintf(stderr, "frank_app: now sending commands to stop RT-tasks\n"); /* stop the tasks */ MSG.command = STOP_TASK; MSG.tarefa = 0; if (write(ctl, &MSG, sizeof(MSG)) < 0) { fprintf(stderr, "Can't send a command to RT-task\n"); exit(1); } MSG.tarefa = 1; if (write(ctl, &MSG, sizeof(MSG)) < 0) { fprintf(stderr, "Can't send a command to RT-task\n"); exit(1); } return 0; }
# ifndef __PLACA16_H # define __PLACA16_H #define LPT 0x378 /*** Variaveis para a Primeira placa DAD1601 ***/ # define AD_BASE ( 0x0300 ) // Endereco base da placa ( default ) # define AD0 ( AD_BASE ) // end. LSB do AD + MUX channel # define AD1 ( AD_BASE + 1 ) // end. MSB do AD # define AD_MUX ( AD_BASE + 2 ) // end. para selecionar canal # define DIO ( AD_BASE + 3) // end. do port i/o digital # define DA0LB ( AD_BASE + 4) # define DA0HB ( AD_BASE + 5) # define AD_STATUSA ( AD_BASE + 8 ) // end. de status # define AD_CTRL ( AD_BASE + 9 ) // end. de controle # define AD_CNTEN ( AD_BASE + 0x0A ) // end. de counter enable # define AD_GAIN ( AD_BASE + 0x0B ) // end. de ganhos # define AD_TIMER0 ( AD_BASE + 0x0C ) // end. de Timer 0 # define AD_TIMER1 ( AD_BASE + 0x0D ) // end. de Timer 1 # define AD_TIMER2 ( AD_BASE + 0x0E ) // end. de Timer 2 # define AD_TIMERCTL ( AD_BASE + 0x0F ) // end. de ctrol Timer # define AD_DISARM ( AD_BASE + 0x404 ) // end. de counter dis # define AD_BMDEN ( AD_BASE + 0x405 ) // end. de Enable busrst # define AD_M1600 ( AD_BASE + 0x406 ) // end. de 1600 mode # define AD_STATUSB ( AD_BASE + 0x407 ) // end. de status B # define AD_POL (2048) // Bipolar // # define AD_POL (0) // Unipolar #define AD_CANAL (0x00) //Sao 16 canais de 0x00 a 0x0F //#define AD_CANAL (0x0F) #define AD_INTE (0x00) //Desabilita (0x00) ou habilita (0x10) interrupcoes da placa AD //#define AD_INTE (0x10) #define AD_LEVEL (0x00) //Define o nível de interrupcao para o processamento das mesmas //#define AD_LEVEL (0x07) // vai de (0x00) a (0x07) #define AD_DMAE (0x00) //Desabilita (0x00) ou habilita (0x01) DMA //#define AD_DMAE (0x01) #define AD_TRIG (0x00) // Trigger Source. Se 0x00 ou 0x01 eh por software, se 0x02 eh pelo Trig 0 //#define AD_TRIG (0x03) // e se eh 0x03 pelo timer #define AD_GANHO (0x00) //Define o ganho do DA de 1 (0x00) a 500 (0x03) //#define AD_GANHO (0x03) #define AD_BRATE (0x00) //Define a taxa de conversoes durante uma amostragem do tipo Burst. //#define AD_BRATE (0x1F) //Esses bits so sao utilizados se o Burst Mode estiver habilitado // BURST RATE = 1MHz / ((4 * BURST RATE VALUE) +2) #define AD_BLEN (0x00) //Define o número de conversoes durante cada trigger numa aquisicao do tipo Burst //#define AD_BLEN (0x0F) // Esses bits so sao utilizados se o Burst Mode estiver habilitado #define AD_CLOCK (0x00) //Se 0x00 habilita Contador/Timers. //#define AD_CLOCK (0x03) //Se 0x01 habilita Contador/Timers somente se TRG0/IP0 = 0. //Se 0x10 habilita clock de 100kHz e Contador/Timers. //Se 0x11 habilita clock de 100kHz e Contador/Timers somente se TRG0/IP0 = 0. #define AD_INICIO (0x00) //Canal inicial de conversao AD //#define AD_INICIO (0x03) #define AD_FIM (0x00) //Canal final de conversao AD //#define AD_FIM (0x03) #define AD_HAB (0x00) //Habilita (0x00) ou desabilita conversoes (0x40) //#define AD_HAB (0x40) /** Placa DDA-06 ****/ /* # define DA_BASE_DFL ( 0x2E0 ) # define DA0_LOW ( DA_BASE ) # define DA0_HIGH ( DA_BASE + 1 ) # define DA1_LOW ( DA_BASE + 2) # define DA1_HIGH ( DA_BASE + 3 ) # define DA2_LOW ( DA_BASE + 4 ) # define DA2_HIGH ( DA_BASE + 5 ) # define DA3_LOW ( DA_BASE + 6 ) # define DA3_HIGH ( DA_BASE + 7 ) # define DA_PA ( DA_BASE + 0xC ) # define DA_PB ( DA_BASE + 0xD ) # define DA_PC ( DA_BASE + 0xE ) # define DA_CTRL ( DA_BASE + 0xF ) # define DA_FAIXA ( 4096 ) // 12 bits # define DA_POL ( 2048 ) // Bipolar // (0) // Unipolar # define DA_VMAX ( 5 ) # define DA_VMIN ( 0 ) // Suponhe que todos os DA // estam na mesma faixa. */ /*** Para a placa mae do PC ***/ # define CTRL_8259 ( 0x0020 ) // Controle do 8259 # define MASK_8259 ( 0x0021 ) // Mascara do 8259 // Configuracao de canais da placa A/D # define NUM_CANAIS ( 16 ) # define CANAL_INICIAL ( 15 ) # define CANAL_FINAL ( 0 ) # define HARD_INTR ( 7 ) # endif
/* Driver para controle de um duplo integrador. Daniela 16/05/2000 */ /* This program makes the interface between a GUI (not RT) and AD/DA boards. It looks for work with 2 kinds of AD/DA boards and V1.1 and V2.2 of RTL, both choosed through #define */ #ifndef MODULE #define MODULE #endif #ifndef __KERNEL__ #define __KERNEL__ #endif #ifndef __RT__ #define __RT__ #endif // Define se o drive sera para a versao 1.1 ou 2.2 do RTLinux e se sera para a placa DAS1600 ou AIO (STD) #define V1 //Escolhe entre as versoes 1.1 e 2.2 do RTL //#define V2 //#define AIO //Escolhe entre as placas AD/DA AIO(STD) e DAS1601 #define DAS1600 #define OSC //Define se havera uma onda quadrada na porta paralela para teste #include <linux/module.h> #include <linux/kernel.h> #include <linux/version.h> #include <linux/errno.h> #include <linux/cons.h> #include <linux/ioport.h> #include <linux/string.h> #include <pthread.h> #include <asm/io.h> #include <rtl_sched.h> #include <rtl_fifo.h> #include "declaracoes.h" #ifdef AIO #include "placa_ai.h" // header com os enderecos da placa AD/DA do STD #endif #ifdef DAS1600 #include "placa16.h" // header com os enderecos da placa AD/DA DAS1600 #endif #ifdef V1 RT_TASK tarefa[2]; #endif #ifdef V2 pthread_t tarefa[2]; #endif //Definicao das variaveis de tempo #ifdef V2 hrtime_t RT_TICKS_CTRL; //Variavel utilizada para determinar o tempo de amostragem para o controle hrtime_t RT_TICKS_SAVE; //Variavel utilizada para determinar o tempo de amostragem para salvar os dados nas fifos hrtime_t ONDA_QUADRADA; hrtime_t ONDA_QUADRADA2; #endif #ifdef V1 RTIME RT_TICKS_CTRL; //Variavel utilizada para determinar o tempo de amostragem para o controle RTIME RT_TICKS_SAVE; //Variavel utilizada para determinar o tempo de amostragem para salvar os dados nas fifos RTIME ONDA_QUADRADA; RTIME ONDA_QUADRADA2; #endif parametros_PID PID; // Ganhos do PID mensagens MSG; // Mensagens de inicializacao e mudanca de periodo float uf, vf; int ub; int valor; float tensao; float h; float ek, pk, dk, otensao, ref; float ik; float N=20.0; float Nh; float escala; //escala de medicao: VADPP/4095.0 (converte para volts) int a=1; int dispara_ad; //String que dipara o AD int controle_ad; //String que controla o funcionamento da placa AD/DA int range; // Range de enderecos ocupados pelo AD //float meio_escala; //int num_bits; float FREQHZ; float VADPP; int contador; //Contador que controla a onda quadrada int sinal; // Sinal da onda quadrada #ifdef AIO static int AIO_port_base=0x0300; //Se zero a base sera lida do arquivo .h #endif #ifdef DAS1600 static int das1601_port_base=0; //Se zero a base sera lida do arquivo .h #endif void *lei_controle(void *t) { while (1) { #ifdef V2 pthread_wait_np(); // Espera a proxima interrupcao #endif #ifdef V1 rt_task_wait(); // Espera a proxima interrupcao #endif #ifdef DAS1600 valor = ( (inb(AD0) & 0x0F0) >> 4 | ((inb(AD1) & 0x0FF) << 4)); // Aquisicao da posicao tensao = ((float) (valor - AD_POL))*escala; #endif #ifdef AIO valor = ( (inb(AD0) & 0x0FF) | ((inb(AD1) & 0x00F) << 8)); // Aquisicao da posicao tensao = ((float) (valor - AD_POL))*escala; #endif // Nao Entendi.Muito complicado if (contador > ONDA_QUADRADA2 && contador <= ONDA_QUADRADA) // Gera um onda quadrada ou retangular? sinal = -1; else if (contador > ONDA_QUADRADA) { sinal = 1; contador=0; } contador++; // Lei de controle ref = PID.ref + PID.sw_amplitud*sinal; // A referencia e uma constante mais a amplitude // da onda quadrada * sinal aplicado ek = ref - tensao; //tensao e a leitura atual da posicao (convertida em valores de tensao) pk = PID.kp*ek; //termo proporcional do controle dk = PID.kd/(PID.kd+Nh)*(dk - PID.kp*N*(tensao-otensao)); //termo derivativo do controle vf = pk + ik + dk; //tensao de controle // Saturacao do controle ub \in [0,4095] if( vf > 1.0) uf=1.0; else if (vf < -1.0) uf=-1.0; else uf=vf; if (AD_POL==0) { // a placa do DAS está ajustada para trabalhar de 0 a +10 volts ub= (int)uf*4095;} else { ub = ((int)(uf*2047.0)) + 2048;} // a placa do DAS está ajustada para trabalhar de -10 a +10 volts // Sinal de Controle (+/- 5 volts setados na placa AD/DA) // Escreve o controle no DA #ifdef DAS1600 outb((((ub&0x00F) << 4) & 0xF0), DA0LB); outb((((ub&0xFF0) >> 4) & 0xFF), DA0HB); #endif #ifdef AIO outb(((ub&0x0FF) & 0xFF), DA0LSB); outb((((ub&0xF00) >> 8)& 0x0F),DA0MSB); #endif // Calculo dos estados do PID ik += PID.kp*PID.ki*h*ek; //acao integral otensao = tensao; // prepara a variavel de tensao anterior #ifdef OSC //Produz uma onda quadrada na porta paralela (para teste) outb(a, LPT); a = ~a & 0x01; #endif OSC // outb(0, AD0); //dispara o AD para a proxima conversao #ifdef AIO dispara_ad = (((AD_GANHO << 5)|AD_CANAL)& 0xFF); outb(dispara_ad,AD_MUX); #endif #ifdef DAS1600 dispara_ad = ((AD_CANAL)& 0x0F); outb(dispara_ad,AD0); #endif } } void *salvar_dados(void *t) { while (1) { #ifdef V2 pthread_wait_np(); // Espera a proxima interrupcao #endif #ifdef V1 rt_task_wait(); // Espera a proxima interrupcao #endif /* Salva posicao e controle nas FIFO */ rtf_put(0, &tensao, sizeof(float)); rtf_put(1, &ref, sizeof(float)); rtf_put(3, &uf, sizeof(float)); } } int mod_pid(unsigned int fifo) { rtf_get(fifo, &PID, sizeof(parametros_PID)); //Atualiza a struct PID com os novos dados ik=0.0; return(0); } int comando(unsigned int fifo) { int err; #ifdef V1 RTIME now; #endif #ifdef V2 struct sched_param sched_param; hrtime_t now; #endif while ((err= rtf_get(fifo, &MSG, sizeof(MSG)))== sizeof(MSG)) { //Atualiza a struct MSGS com os novos dados switch (MSG.command){ case START_TASK: #ifdef V2 now = gethrtime(); pthread_make_periodic_np(tarefa[MSG.tarefa], now + 1 * NSECS_PER_SEC, MSG.period); #endif #ifdef V1 now = rt_get_time(); rt_task_make_periodic(&tarefa[MSG.tarefa], now,MSG.period); #endif case STOP_TASK: #ifdef V2 pthread_suspend_np(tarefa[MSG.tarefa]); #endif #ifdef V1 rt_task_suspend(&tarefa[MSG.tarefa]); #endif default: return -EINVAL; } } if (err != 0){ return -EINVAL; } return 0; } int init_ad(unsigned int port, unsigned int range) { int err=0; // Verifica se os enderecos necessarios para o AD estao disponiveis // Nao entendi check_region e request_region if ((err=check_region(port,range)) < 0) return err; /* busy */ request_region(port,range,"Placa AD/DA"); /* always succeeds */ // Programacao do AD // Programacao do AD para a placa DAS1601 #ifdef DAS1600 controle_ad = (((AD_INTE & 0x01) << 7)| ((AD_LEVEL & 0x07) << 4) | ((AD_DMAE & 0x01) << 2) | (AD_TRIG & 0x03)); outb(controle_ad, AD_CTRL); /* 0 INT xxx INT LEVEL = HardInt x Doesn't matter 0 DMAE = Desabled 0x Trigger Source = Timer */ controle_ad = (((AD_BRATE & 0x1F) << 2) | (AD_GANHO & 0x03)); outb(controle_ad, AD_GAIN); /* Seta ganhos */ controle_ad = (((AD_BLEN & 0x0F) << 4) | (AD_CLOCK & 0x03)); outb(controle_ad, AD_CNTEN); /* Contador 1 e 2 habilitados */ controle_ad = (((AD_FIM & 0x0F) << 4)| (AD_INICIO & 0x0F)); outb(controle_ad, AD_MUX); /* Conversao do Canal 0 somente */ //outb(0x11, AD_MUX); /*Conversao do Canal 1 somente */ outb(AD_HAB, AD_DISARM); /* Habilita Conversao */ #endif //Programacao do AD para a placa AD do STD #ifdef AIO controle_ad = (((AD_GANHO << 5)|AD_CANAL)& 0xFF); outb(controle_ad,AD_MUX); #endif return(err); } int init_ctrl(void) { //num_bits = 12; //meio_escala = (2^num_bits)/2; FREQHZ = 100.0; //Base para a frequencia de amostragem h = 1.0/FREQHZ; Nh = N*h; escala = VADPP/4095; // Ambas as placas tem 12 bits de resolucao ik=0.0; contador=0; //Contador que controla a onda quadrada sinal = 1; // Sinal inicial da onda quadrada //Inicializa as variaveis de tempo #ifdef V2 RT_TICKS_CTRL = HRTICKS_PER_SEC/ FREQHZ; RT_TICKS_SAVE = 4 * RT_TICKS_CTRL; ONDA_QUADRADA = 20 * FREQHZ; ONDA_QUADRADA2 = 20 * FREQHZ / 2; #endif #ifdef V1 RT_TICKS_CTRL = HRTICKS_PER_SEC/ FREQHZ; //Deve ter que mudar aqui! RT_TICKS_SAVE = 4 * RT_TICKS_CTRL; ONDA_QUADRADA = 20 * FREQHZ; ONDA_QUADRADA2 = 20 * FREQHZ / 2; #endif // Inicializa as referencias PID.ref=0; PID.sw_amplitud=0.5; return 0; } int init_module(void) { int adstatus; int err; int c[5]; #ifdef V1 RTIME now; #endif //#ifdef V2 // struct sched_param sched_param; // hrtime_t now; //#endif #ifdef DAS1600 int base = das1601_port_base ? das1601_port_base : AD_BASE; //Determina se o endereco base sera o mesmo do arquivo .h range=0x20; if (AD_POL==0) { VADPP = 10.0;} // a placa do DAS está ajustada para trabalhar de 0 a +10 volts else { VADPP = 20.0;} // a placa do DAS está ajustada para trabalhar de -10 a +10 volts #endif #ifdef AIO int base = AIO_port_base ? AIO_port_base : AD_BASE; //Determina se o endereco base sera o mesmo do arquivo .h range=0x20; VADPP=5.0; // a placa do STD trabalha de 0 a 5 volts #endif if((err=init_ad(base, 0x20)) != 0 ) { //Se o AD nao puder ser inicializado retorna uma mensagem de erro printk(KERN_INFO "I/O Address already used\n"); printk("I/O Address already used\n"); return(-1); } init_ctrl(); //Inicializa as variaveis de tempo, frequencia, escala, etc. printk(KERN_INFO "di.c:2.0 09/05/00 [EMAIL PROTECTED]\n"); #ifdef DEBUG printk(KERN_INFO "di.c: Sampling time: %i*nanoseg \n", RT_TICKS_CTRL); printk(KERN_INFO "di.c: Saving interval: %i*nanoseg \n", RT_TICKS_SAVE); #endif // Nao exporta as variaveis globais para o kernel //register_symtab(0); // Cria as FIFOs c[0] = rtf_create(0, 1000); // y c[1] = rtf_create(1, 1000); // status ad c[2] = rtf_create(2, 1000); // parametros PID c[3] = rtf_create(3, 1000); // u c[4] = rtf_create(4, 1000); //comandos para o tarefa0 (lei_controle) e para o tarefa1 (salvar_dados) printk("Fifo return 0=%d 1=%d 2=%d 3=%d 4=%d\n",c[0],c[1],c[2],c[3],c[4]); // Le o status do AD #ifdef DAS1600 adstatus = inb(AD_STATUSA); #endif #ifdef AIO adstatus = (inb(AD1)); adstatus = ((adstatus & 0xC0) >> 6); #endif //rtf_put(1, &adstatus, sizeof(int)); rtf_put(1, &FREQHZ, sizeof(float)); // Cria as threads #ifdef V2 pthread_create(&tarefa[0], NULL,lei_controle, (void *)1); sched_param.sched_priority = 1; pthread_setschedparam (tarefa[0], SCHED_FIFO, &sched_param); pthread_create (&tarefa[1], NULL, &salvar_dados, (void *)1); sched_param.sched_priority = 4; pthread_setschedparam (tarefa[1], SCHED_FIFO, &sched_param); now = gethrtime(); /* the 2 tarefas run periodically with an offset of 100 time units -> Nao entendi o comentario */ pthread_make_periodic_np(tarefa[0], now + 1 * NSECS_PER_SEC, RT_TICKS_CTRL); pthread_make_periodic_np(tarefa[1], now + 2 * NSECS_PER_SEC, RT_TICKS_SAVE); #endif #ifdef V1 rt_task_init(&tarefa[0],(void *)lei_controle,1,3000,1); rt_task_init(&tarefa[1],(void *)salvar_dados,1,3000,4); printk("Tarefas inicializadas\n"); now = rt_get_time(); /* the 2 tasks run periodically with an offset of 100 time units */ rt_task_make_periodic(&tarefa[0], now,120000); rt_task_make_periodic(&tarefa[1], now + 3000,120000); #endif // Cria o handler de modificacao dos parametros do PID rtf_create_handler(2,&mod_pid); //Toda vez que ha uma modificacao da fifo 2 mod_pid eh chamada rtf_create_handler(4,&comando); //A rotina comando le a fifo 4 e determina o inicio e o fim das tarefas return 0; } void cleanup_module(void) { // int r[2]; /* release adc region. Before that write zero to the DA */ #ifdef DAS1600 outb(0x00,DA0LB); outb(0x00,DA0HB); #endif #ifdef AIO outb(0x00,DA0LSB); outb(0x00,DA0MSB); #endif rtf_destroy(0); rtf_destroy(1); rtf_destroy(2); rtf_destroy(3); rtf_destroy(4); #ifdef V2 pthread_delete_np(tarefa[0]); pthread_delete_np(tarefa[1]); #endif #ifdef V1 rt_task_delete(&tarefa[0]); rt_task_delete(&tarefa[1]); #endif release_region(AD_BASE, range); }
//#define COMMAND_FIFO 4 #define START_TASK 1 #define STOP_TASK 2 typedef struct { int command; int tarefa; int period; }mensagens ; typedef struct { float kd; float kp; float ki; float ref; float sw_amplitud; }parametros_PID;