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;