Ecco il primo problema

di il
5 risposte

Ecco il primo problema

Ho questo cavolo di codice, che non riesco a capire..
o meglio un pò si, ma alcune cose mi sono oscure!potete aiutarmi???:
(ricordo che sono inesperta di c++)
Devo "dividere" la parte relativa solo alla radio, all I2C e loro funzionamenti da quellla relativa a tt il resto!
ecco il codice:


/*
9-27-09
Copyright Spark Fun Electronics© 2009
Nathan Seidle
spark at sparkfun.com

Example Interface to AR1000
Using ATmega168 at 8MHz
*/

#include <stdio.h>
#include <avr/io.h>
#include "i2c.h"

#define FOSC 8000000
#define BAUD 9600
#define MYUBRR 103

#define sbi(port, bit_mask) ((port) |= (uint8_t)(1 << bit_mask))
#define cbi(port, bit_mask) ((port) &= (uint8_t)~(1 << bit_mask))

#define STATUS_LED 5

#define AR1000_W 0x20 //Write address of AR1000
#define AR1000_R 0x21//read address

#define ADDR_STATUS 0x13 // the address of status register
#define MASK_STC (1<<5) //0x0020 // Seek/Tune/PowerOn complete D5 in adress 13H
#define MASK_SF (1<<4) //0x0010 // Seek Fail D4 in address 13H
#define MASK_ST (1<<3) //0x0008 // Stereo D3 in address 13H
#define MASK_READCHAN 0xFF80 // D7~D15 in address 13H
#define SHIFT_READCHAN 7

#define AR1000_MUTE_ON { uint16_t temp = ar1000_read(1); ar1000_write(1, temp | (1<<1)); } //Reg_Data[1].BIT.B1 = ON;
#define AR1000_MUTE_OFF { uint16_t temp = ar1000_read(1); ar1000_write(1, temp & ~(1<<1)); } //Reg_Data[1].BIT.B1 = OFF;}

#define AR1000_TUNE_ON { uint16_t temp = ar1000_read(2); ar1000_write(2, temp | (1<<9)); } // Reg_Data[2].BIT.B9 = ON;
#define AR1000_TUNE_OFF { uint16_t temp = ar1000_read(2); ar1000_write(2, temp & ~(1<<9)); } //Reg_Data[2].BIT.B9 = OFF;

#define AR1000_SEEK_ON { uint16_t temp = ar1000_read(3); ar1000_write(3, temp | (1<<14)); } //Reg_Data[3].BIT.B14 = ON;}
#define AR1000_SEEK_OFF { uint16_t temp = ar1000_read(3); ar1000_write(3, temp & ~(1<<14)); } //Reg_Data[3].BIT.B14 = OFF;}

//I2CDEBUG will turn on '!' serial characters and TWI status debugging
//Comment out the define to turn off debug characters
//#define I2CDEBUG

//Define functions
//======================
void i2c_SendStart(void);
void i2c_SendStop(void);
void i2c_WaitForComplete(void);
unsigned char i2c_SendByte(unsigned char data);
unsigned char i2c_ReceiveByte(unsigned char ackFlag);

void ioinit(void);
static int uart_putchar(char c, FILE *stream);
uint8_t uart_getchar(void);
int uart_gethex(uint8_t length_to_read);

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

void delay_ms(uint16_t x);
void delay_us(uint16_t x);

uint16_t ar1000_read(uint8_t address);
void ar1000_write(char reg_address, uint16_t reg_value);
void ar1000_readall(void);
void ar1000_write_array(void);
void ar1000_init(void);
void ar1000_setvolume(uint8_t volume_level);
void ar1000_status(void);
void ar1000_tuneto(uint16_t freq_kHz);
void ar1000_tune_hilo(uint16_t freq_kHz);
void ar1000_seek(void);
uint16_t ar1000_rssi(void);

void print_array(void);
void set_array_value(void);

//RSSI 49-54 is pretty good. 34-41 is complete static

//Register conaining default values for the AR1000, these are the default values from the programming guide.
uint16_t register_values[18] =
{

0xFFFF, //R0
0x5B15, //R1
0xF4B9, //R2 Tune/Channel
0x8012, //R3 seekTHD = 18
0x0400, //R4
0x28AA, //R5
0x4400, //R6
0x1EE7, // R7
0x7141, // R8
0x007D, // R9
0x82C6, // R10 disable wrap
0x4F55, // R11. <--- (disable xo_output)
0x970C, // R12.
0xB845, // R13
0xFC2D, // R14 : Volume control 2
0x8097, // R15
0x04A1, // R16
0xDF6A // R17

//Bad values from the Airoha example code - they don't work for me
/*0xFF7B, // R0 -- the first writable register . (disable xo_en)
0x5B15, // R1
0xD0B9, // R2
0xA010, // R3 seekTHD = 16
0x0780, // R4
0x28AB, // R5
0x6400, // R6
0x1EE7, // R7
0x7141, // R8
0x007D, // R9
0x82C6, // R10 disable wrap
0x4F55, // R11. <--- (disable xo_output)
0x970C, // R12.
0xB845, // R13
0xFC2D, // R14 : Volume control 2
0x8097, // R15
0x04A1, // R16
0xDF6A */ // R17
};

// volume control (increasing)
unsigned char AR1000vol[22] =
{
0x0F, // step 0
0xCF, // 1
0xDF, // 2
0xEF, // 3
0xFF, // 4
0xEE, // 5
0xFE, // 6
0xED, // 7
0xFD, // 8
0xFB, // 9
0xFA, // 10
0xF9, // 11
0xF7, // 12
0xE6, // 13
0xF6, // 14
0xE5, // 15
0xF5, // 16
0xE3, // 17
0xF3, // 18
0xF2, // 19
0xF1, // 20
0xF0 // 21 <------ default setting
};

//======================

int main (void)
{
ioinit(); //Setup IO pins and defaults

char option;
char vol = 21;

while(1)
{
printf("\n\n----AR1000 Configuration----");
printf("\n1) Send array values");
printf("\n2) Read array values");
printf("\n3) Set array value");
printf("\n4) Read All Register values");
printf("\n5) Begin Seek");
printf("\n6) Tune to 97.3");
printf("\n7) Check Status");
printf("\n8) Raise volume");
printf("\n9) Lower volume");
printf("\na) Check RSSI");
printf("\nb) Special tune hi/low to 97.3");

option = uart_getchar();

if(option == '1')
{
printf("\n\n\tSending Array Values\n");
ar1000_write_array();
printf("\n\t\tDone\n");
}

else if(option == '2')
{
print_array();
}

else if(option == '3')
{
set_array_value();
}

else if(option == '4')
{
printf("\n\n\tRead All\n");
ar1000_readall();
}

else if(option == '5')
{
ar1000_seek();
}

else if(option == '6')
{
printf("\n\t Tune to 97.3\n");
ar1000_tuneto(973);
}

else if(option == '7')
{
ar1000_status();
}

else if(option == '8')
{
if(vol < 21) ar1000_setvolume(vol++);
printf("\nVolume = %02d", vol);
}
else if(option == '9')
{
if(vol > 0) ar1000_setvolume(vol--);
printf("\nVolume = %02d", vol);
}
else if(option == 'a')
{
option = ar1000_rssi();
printf("\nRSSI = %d", option);
}
else if(option == 'b')
{
ar1000_tune_hilo(973);
}

else
printf("\n\nChoice = %c", option);
}

return(0);
}

uint16_t ar1000_rssi(void)
{
#define ADDR_RSSI 0x12
#define MASK_RSSI 0xFE00
#define SHIFT_RSSI 9

uint16_t rssi;

rssi = ar1000_read(ADDR_RSSI);
rssi &= MASK_RSSI;
rssi >>= 9;s

return(rssi);
}

// Volume Control
// There are two different fields about volume control in AR1000F
// Volume : D7~D10 in register R3
// Volume2 : D12~D15 in register R14
// 22 combinations of (volume2 + volume) are recommended.
void ar1000_setvolume(uint8_t volume_level)
{
uint16_t reg3, reg14;

reg3 = ar1000_read(3) & 0xF87F; //Zero out bits D7-D10
reg3 |= ( (AR1000vol[volume_level] & 0x0F) << 7); //Mask in D7-D10

reg14 = ar1000_read(14) & 0x0FFF; //Zero out bits D12-D15
reg14 |= ((AR1000vol[volume_level] & 0xF0) << ; //Mask in D12-D15

ar1000_write(3, reg3);
ar1000_write(14, reg14);
}

//Tunes the AR1000 to a given station.
//Calculate AR1000 CHAN id : Freq (MHz) = 69 + 0.1*CHAN
//Example, sending 973 will tune to 97.3MHz
void ar1000_tuneto(uint16_t freq_kHz)
{
uint16_t channel, temp;

/*
1) Set hmute Bit
2) Clear TUNE Bit
3) Clear SEEK Bit
4) Set BAND/SPACE/CHAN Bits
5) Enable TUNE Bit
6) Wait STC flag (Seek/Tune Comlete, in Status Register
7) Clear hmute Bit
8) Update Functions (optional)
*/

//Clear tune bit
AR1000_TUNE_OFF;

//Set Channel
channel = freq_kHz - 690;
temp = ar1000_read(2); //Read
temp &= 0xFE00; //Mask
temp |= channel;
ar1000_write(2, temp); //Write

//Enable tune bit
AR1000_TUNE_ON;

//Wait for tune to stabilize (STC flag)
temp = 0;
while(temp == 0)
{
temp = ar1000_read(ADDR_STATUS) & MASK_STC;
printf("!");
}

}

//This is some weird function in AR1000 example code provided by Airoha
//Looks like it takes the RSSI into account and then fine tunes the station
//I can't hear much of a difference, but it looks fancy.
void ar1000_tune_hilo(uint16_t freq_kHz)
{
uint16_t temp;

AR1000_MUTE_ON; //Set mute ON before TUNE
AR1000_SEEK_OFF; //Clear seek

//Read Low-Side LO Injection
//R11 --> clear D15, clear D0/D2, D3 is the same as defaultdsd
temp = ar1000_read(11) & 0x7FFA;
ar1000_write(11, temp);

//TUNE to FreqKHz with current setting
ar1000_tuneto(freq_kHz); //This function turns on TUNE and waits for STC flag
//Low-side TUNE Ends

printf("\nLow complete");

uint16_t status = ar1000_read(ADDR_RSSI);
uint16_t rssi = (status & MASK_RSSI);

printf("\nRSSI 1 = %d", rssi >> SHIFT_RSSI);

//Read Hi-Side LO Injection
// R11-->set D15, set D0/D2, D3 is the same as default
temp = ar1000_read(11) | 0x8005;
ar1000_write(11, temp);

//TUNE to FreqKHz with current setting
ar1000_tuneto(freq_kHz); //This function turns on TUNE and waits for STC flag
//High-side TUNE Ends

printf("\nHigh complete");


status = ar1000_read(ADDR_RSSI);
printf("\nRSSI 2 = %d", (status & MASK_RSSI) >> SHIFT_RSSI);
rssi = rssi - (status & MASK_RSSI);
if (rssi < 0) //errata in 0.82
{
// LO
// R11--> clear D15, set D0/D2, D3 is the same as default
temp = (ar1000_read(11) & 0x7FFF) | 0x0005;
ar1000_write(11, temp);
}
else
{
//HI
//R11--> set D15, clear D0/D2, D3 is the same as default
temp = (ar1000_read(11) | 0x8000) & 0xFFFA;
ar1000_write(11, temp);
}

//Fine-tune!!
//TUNE to FreqKHz with current setting
ar1000_tuneto(freq_kHz); //This function turns on TUNE and waits for STC flag

AR1000_MUTE_OFF;

printf("\nLow/Hi tuning complete");

}

//Starts scanning the stations for a minimum set threshold. I found the bit to enable
//wrapping so the every time the function is called, it searches up, and wrap back to 88MHz
//if it doesn't find a good station.
void ar1000_seek(void)
{
#define ADDR_SEEK_SETTING 0x11
#define SEEK_SETTING 0x2000
#define SEEK_MASK 0xC3FF
#define SEEK_TH_MASK 0xFF80
#define SEEK_TH 5 //A higher threshold causes stronger stations to be found

uint16_t temp;

char space = 1; //0.1MHz scanning
char updown = 1; //Seek up
char band = 0; //US/Europe radio band

AR1000_MUTE_ON;
AR1000_TUNE_OFF;
AR1000_SEEK_OFF;

//Enable wrap during seek - I found bit D3 enables wrap, by trial and error. Seems to work
temp = ar1000_read(10) | (1<<3); //0x82C6 = 1000 0010 1100 0110
ar1000_write(10, temp);

//Setting before seek
temp = (ar1000_read(17) & SEEK_MASK) | SEEK_SETTING;
ar1000_write(17, temp);

printf("\n\nBegin searching:");

AR1000_SEEK_ON;
temp = ar1000_read(3);
if(space == 1) temp |= (1<<13); //Set space
if(updown == 1) temp |= (1<<15); //Set seek up or down
temp = (temp & 0xE7FF) | band; //Set Band
temp &= SEEK_TH_MASK; //Clear out the seek threshold
temp |= SEEK_TH; //Set threshold
ar1000_write(3, temp);

//Wait for tune to stabilize (STC flag)
temp = 0;
while(temp == 0)
{
temp = ar1000_read(ADDR_STATUS) & MASK_STC;
printf(".");
}

temp = ar1000_read(ADDR_STATUS) & MASK_SF;
if(temp != 0)
{
printf("\nSeek failed!");
return;
}
printf("\nSeek success!");

temp = ar1000_read(ADDR_STATUS) & MASK_READCHAN;
uint16_t freq_kHz = 690 + (temp >> SHIFT_READCHAN); //Determine what channel we found

printf("\nNow on channel %d.%dMHz", freq_kHz / 10, freq_kHz % 10);

//Restore setting after seek
ar1000_write(17, register_values[17]);

//Fine-tune with auto hilo rejection
ar1000_tune_hilo(freq_kHz);

AR1000_MUTE_OFF;
}

//This function reads in an address and array value, and stores the value into the array address
//it does not change the values in the ar1000, you will need to use the "send array valus"
//function in order to send the new values to the ar1000
void set_array_value(void)
{
uint8_t array_address;
uint16_t array_value;

printf("\n\nArray Address (2 digit hex value): ");
array_address = uart_gethex(2);
printf("\nArray Address is: %x", array_address);

printf("\n\nArray value (4 digit hex value): ");
array_value = uart_gethex(4);
printf("\nArray value is: %x", array_value);

register_values[array_address] = array_value;
}

void print_array(void)
{
//prints the values of the array that is used to control the radio

printf("\n\nArray position: value\n");

for(int i = 0 ; i < 18 ; i++)
printf("0x%.2X: 0x%.4X\n", i, register_values);
}

//Reads a memory register from the AR1000
uint16_t ar1000_read(uint8_t address_to_read)
{
char byte1 = 0, byte2 = 0;
char ack;

AGAIN:
i2c_SendStart(); //Send start condition
ack = i2c_SendByte(AR1000_W); //Send slave device address with write
ack &= i2c_SendByte(address_to_read); //Send address to read
if(ack == 0)
{
#ifdef I2CDEBUG
printf("!"); //No ACK!
#endif
goto AGAIN;
}


i2c_SendStart(); //Send start condition
i2c_SendByte(AR1000_R); //Ask device to read the value at the requested address

if(inb(TWSR) == TW_MR_SLA_ACK)
{
byte1 = i2c_ReceiveByte(TRUE);
byte2 = i2c_ReceiveByte(TRUE);
}
else
{
// device did not ACK it's address,
// data will not be transferred
// return error
//retval = I2C_ERROR_NODEV;
printf("\n\tAck failed!");
}

i2c_SendStop();

//Combine two bytes into one 16-bit word
int16_t temp = byte1 << 8;
temp |= byte2;

return(temp);
}

void ar1000_write(char reg_address, uint16_t reg_value)
{
char ack;
uint8_t value1 = (reg_value & 0xFF00) >> 8;
uint8_t value2 = (reg_value & 0x00FF);

AGAIN:
i2c_SendStart(); //Send start condition
ack = i2c_SendByte(AR1000_W);
ack &= i2c_SendByte(reg_address); //Send address to write to
ack &= i2c_SendByte(value1); //Send the two data bytes to be stored
ack &= i2c_SendByte(value2);
i2c_SendStop();

if(ack == 0)
{
#ifdef I2CDEBUG
printf("!"); //No ACK!
#endif
goto AGAIN;
}
}

void ar1000_write_array(void)
{
// This code writes the array values to the ar1000, it is used to calibrate the ar1000
// on power up and it can send the modified array values needed for the seeking tuning etc

//The example AR1000 code disables the analog and digital blocks
// then write to the 0x01 to 0x11 registers
// then enables the analog and digital blocks - so that's what we will do as well

//Write the first register
ar1000_write(0, register_values[0] & 0xFFFE); //<--- Notice we force the enable bit to zero

for(int i = 1 ; i < 18 ; i++)
ar1000_write(i, register_values); //Write registers 1 to 17 to AR1000

ar1000_write(0, register_values[0]); //Re-write the first register, this will set the enable bit
}

//Reads and prints all 16 registers (16-bits wide) of the AR1000
void ar1000_readall(void)
{
uint16_t x, register_value;
for(x = 0 ; x < 0x1D ; x++)
{
register_value = ar1000_read(x);
printf("0x%.2X: 0x%.4X\n", x, register_value);
}
}

void ar1000_init(void)
{
delay_ms(100); //Wait for power to stabilize

ar1000_write_array(); //Init the AR1000 by writing the initial recommended values

uint16_t status = 0;
while(status == 0)
{
status = ar1000_read(ADDR_STATUS);

printf("\nAR1000 status : 0x%04X", status);

status = status & MASK_STC;
delay_ms(10);
}
}

//Read the status register (0x13) of the AR1000
void ar1000_status(void)
{
uint16_t status;

status = ar1000_read(ADDR_STATUS);

printf("\n\nAR1000 status : 0x%04X", status);

uint16_t channel = status & MASK_READCHAN;
channel >>= SHIFT_READCHAN;
channel += 690;
printf("\nChannel : %02d.%01dMHz", channel / 10, channel % 10);

if(status & MASK_STC)
printf(" (Seek/Tune Complete)");
else
printf(" (Seek/Tune Incomplete)");

if(status & MASK_SF)
printf(" (Seek Fail)");
else
printf(" (Seek Successful)");

if(status & MASK_ST)
printf(" (Stereo)");
else
printf(" (Mono)");
}

//Setup for UART and IO pins
void ioinit (void)
{

//1 = output, 0 = input
DDRB = 0b11111111; //All outputs
DDRC = 0b11111111; //All outputs
//DDRD = 0b11111110; //PORTD (RX on PD0)
stdout = &mystdout; //Required for printf init

UBRR0H = (MYUBRR) >> 8;
UBRR0L = MYUBRR;

UCSR0B = (1<<RXEN0)|(1<<TXEN0);
UCSR0C = (3<<UCSZ00);
UCSR0A = (1<<U2X0);

//Init timer 0 for delay_us timing
//8,000,000 / 8 = 1,000,000
TCCR0B = (1<<CS01); //Set Prescaler to 8. CS01=1

//initialize I2C hardware
TWCR = 0x00;
TWBR = 64;
//TWSR = (1 << TWPS1);
cbi(TWCR, TWEA);
sbi(TWCR, TWEN);

ar1000_init(); //Initialize AR1000
}

static int uart_putchar(char c, FILE *stream)
{
if (c == '\n') uart_putchar('\r', stream);

loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;

return 0;
}

uint8_t uart_getchar(void)
{
while( !(UCSR0A & (1<<RXC0)) );
return(UDR0);
}

//Reads in ASCII characters, converts to decimal and returns decimal variable
//length_to_read is how many characters we want to read, "A33C" would be four characters long
int uart_gethex(uint8_t length_to_read)
{
int decimal_value = 0;
unsigned char input_character;

for(uint8_t x = 0 ; x < length_to_read ; x++)
{
decimal_value <<= 4; //Every time we loop, make room for another 4-bit hex value to be masked into decimal_value

input_character = uart_getchar();
printf("%c", input_character);

//Check the character for validity
if( (input_character >= '0') && (input_character <= '9') )
decimal_value |= (input_character - '0');

else if( (input_character >= 'a') && (input_character <= 'f') )
decimal_value |= (input_character - 'a');

else
{
//We have a non-legal character. Handle the exception however you want
return(0);
}
}

return(decimal_value);
}

//General short delays
//general short delays
//Uses internal timer do a fairly accurate 1us
//Because we are using 16MHz and a prescalar of 8 on Timer0, we have to double x
void delay_us(uint16_t x)
{
while(x > 256)
{
TIFR0 = (1<<TOV0); //Clear any interrupt flags on Timer0
TCNT0 = 0; //Preload Timer0 for 256 clicks. Should be 1us per click
while( (TIFR0 & (1<<TOV0)) == 0);

x -= 256;
}

TIFR0 = (1<<TOV0); //Clear any interrupt flags on Timer0
TCNT0 = 256 - x; //256 - 125 = 131 : Preload Timer0 for x clicks. Should be 1us per click
while( (TIFR0 & (1<<TOV0)) == 0);
}

//General short delays
void delay_ms(uint16_t x)
{
for ( ; x > 0 ; x--)
delay_us(1000);
}

//==========================
//
//I2C functions
//
//==========================

void i2c_SendStart(void)
{
// send start condition
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

i2c_WaitForComplete();
}

void i2c_SendStop(void)
{
// transmit stop condition
TWCR = (1<<TWSTO);
}

void i2c_WaitForComplete(void)
{
// wait for any previous i2c stuff to complete before sending new data
while (!(TWCR & (1<<TWINT)));
}

unsigned char i2c_SendByte(unsigned char data)
{
// save data to the TWDR
TWDR = data;
// begin send
TWCR = (1<<TWINT)|(1<<TWEN);

// wait until transmission completed
while(!(TWCR & (1<<TWINT)));

// check value of TWI Status Register. Mask prescaler bits
uint8_t twst = TWSR & 0xF8;

#ifdef I2CDEBUG
printf("twst: 0x%02X\n\n", twst);
#endif

if( twst == 0x18) return 1; //SKA+W was tranmitted, ACK received
if( twst == 0x28) return 1; //Data was tranmitted, ACK received
return 0;
}

unsigned char i2c_ReceiveByte(unsigned char ackFlag)
{
// begin receive over i2c
if( ackFlag )
{
// ackFlag = TRUE: ACK the recevied data
outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
}
else
{
// ackFlag = FALSE: NACK the recevied data
outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
}

i2c_WaitForComplete();

// retieve received data byte from i2c TWDR
return( inb(TWDR) );
}

5 Risposte

  • Re: Ecco il primo problema

    Vorrei fare delle considerazioni:
    1) Il codice che pubblichi è soggetto a copyright senza dichiarazione di licenza.
    2) E' specifico di un'interfaccia hardware e non legato alla conoscenza sulla programmazione C.
    3) Il codice non è racchiuso nell'apposito tag

    ~Max~
  • Re: Ecco il primo problema

    Allora elimino il post.
    c,mq è tranquillamente scaricabile da internet!non lo mica rubato!
    cmq mi hai fatto sentire malissimo se qst era la tua intenzione.
    lo detto all'inizio, sono inessperta!
    io nn capisco perchè dovete per forza rispondere cosi!vabbe.
    elimino
  • Re: Ecco il primo problema

    Non si tratta che il codice sia scaricabile o rubato...
    In testa al src leggo che c'è un copyright (diritti d'autore) che senza una licenza non si possono interpretare.

    Non voglio spaventare nessuno, Voglio far notare che la licenza deve essere fornita insieme al sorgente per sapere se è possibile modificarlo, copiarlo e/o distribuirlo ecc.. ecc...

    Saluti,
    Max
  • Re: Ecco il primo problema

    Sarò ignorante, ma continuo a non capire!!!
    Che vuol dire???
    Se è scaribile da internet, che il tipo ha messo proprio a disposizione il link dove scaricarlo, pesno che ci dia la possibilità di farlo no??'
  • Re: Ecco il primo problema

    In informatica, open source (termine inglese che significa sorgente aperto) indica un software i cui autori (più precisamente i detentori dei diritti) ne permettono, anzi ne favoriscono il libero studio e l'apporto di modifiche da parte di altri programmatori indipendenti. Questo è realizzato mediante l'applicazione di apposite licenze d'uso.

    http://it.wikipedia.org/wiki/Open_sourc

    http://www.opensource.org/licenses/alphabetica

    Saluti,
    Max
Devi accedere o registrarti per scrivere nel forum
5 risposte