///////////////////////////////////////////////////////////////////////// //// DMTD_01.C //// //// //// //// PIC16F648A embedded software for DMTD DDS Module //// //// //// //// This program simply loads configuration information and the //// //// frequency word into an AD9951 DDS chip and then goes to sleep. //// //// It reads the frequency select switch but does not implement //// //// a user interface. The DDS is clocked at 10 MHz and uses a //// //// x12 clock multiplier. The amplitude is set to maximum and a //// //// LED at RA1 is turned on during the DDS loading process. //// //// //// //// The 16 32-bit frequency word choices are stored in EEPROM at //// //// compile time, are selected with the DIP switches, and could //// //// be changed via a user interface whose code is drafted. //// //// //// //// PIC16F628 Pin assignments: //// //// 1 RA2 Switch 8 10 RB4 DDS SDIO //// //// 2 RA3 Switch 4 11 RB5 DDS SCLK //// //// 3 RA4 Switch 2 12 RB6 ISP CLK //// //// 4 /MCLR Master Clr 13 RB7 ISP DATA //// //// 5 Vss Ground 14 Vdd +5V //// //// 6 RB0 Switch 1 15 RA6 DDS IOSYNC //// //// 7 RB1 RS232 RX 16 CLKIN 10 MHz Clk //// //// 8 RB2 RS232 TX 17 RA0 DDS RESET //// //// 9 RB3 DDS IOUPDATE 18 RA1 LED //// //// //// //// Revision Record: //// //// 03/06/10 Created //// //// 03/07/10 Working //// //// Memory usage: ROM=7% RAM=8% //// //// //// //// //// W. Riley (c) Hamilton Technical Services All Rights Reserved //// //// //// //// //// ///////////////////////////////////////////////////////////////////////// // Device header file #include <16F648A.H> // PIC16F648A Configuration #fuses EC_IO,NOWDT,PUT,NOPROTECT,NOCPD,BROWNOUT,MCLR,NOLVP // 10 MHz Clock setting #use delay(clock=10000000) // RS-232 setting #use rs232(baud=19200, xmit=PIN_A3, rcv=PIN_A4) // Compiler optimization setting #opt 5 // Nominal=5 // PIC Defines #define STATUS 0x03 #define W 0x00 #define INTCON 0x0B #define EECON1 0x9C #define EECON2 0x9D #define EEDATA 0x9A #define EEADR 0x9B #define RP0 0x05 #define RD 0x00 #define WR 0x01 #define WREN 0x02 #define GIE 0x07 #define PIR1 0x0C #define EEIF 0x07 // General Defines #define LOW 0 // Logic low #define HIGH 1 // Logic high // PIC Port macros for DMTD DDS board #define RESET PIN_A0 // DDS Reset Pin 36 #define LED PIN_A1 // LED (H=On) #define SWITCH8 PIN_A2 // Freq select switch 8 weighting #define SWITCH4 PIN_A3 // Freq select switch 4 weighting #define SWITCH2 PIN_A4 // Freq select switch 2 weighting #define IOSYNC PIN_A6 // DDS I/O Sync Pin 37 #define SWITCH1 PIN_B0 // Freq select switch 1 weighting #define RX PIN_B1 // RS-232 Receive #define TX PIN_B2 // RS-232 Transmit #define IOUPDATE PIN_B3 // DDS I/O Update Pin 1 #define SDIO PIN_B4 // DDS SDIO Pin 41 #define SCLK PIN_B5 // DDS Serial Clock Pin 40 #define CLK PIN_B6 // ISP Clock J1 Pin 2 #define DATA PIN_B7 // ISP Data J1 Pin 3 // S/W Revision Number #define DMTD_REV "1.0" // DMTD DDS Module software revision # // EEPROM initializations // Default DDS frequency words // 2100 = 5 MHz + 1 Hz = 0000 // 2108 = 5 MHz + 5 Hz = 0001 // 2110 = 5 MHz + 10 Hz = 0010 // 2118 = 5 MHz + 100 Hz = 0011 // 2120 = 10 MHz + 1 Hz = 0100 // 2128 = 10 MHz + 5 Hz = 0101 // 2130 = 10 MHz + 10 Hz = 0110 // 2138 = 10 MHz + 100 Hz = 0111 // 2140 = 10.23 MHz + 1 Hz = 1000 // 2148 = 10.23 MHz + 5 Hz = 1001 // 2150 = 10.23 MHz + 10 Hz = 1010 // 2158 = 10.23 MHz + 100 Hz = 1011 // 2160 = 13.40134393 MHz + 1 Hz = 1100 // 2168 = 13.40134393 MHz + 5 Hz = 1101 // 2170 = 13.40134393 MHz + 10 Hz = 1110 // 2178 = 13.40134393 MHz + 100 Hz = 1111 // EEPROM map // Address 0 1 2 3 4 5 6 7 8 9 A B C D E F // 2100 0 A A A A A C E 0 A A A A B 5 D // // 2110 0 A A A A C 1 0 0 A A A B 8 A 5 // // 2120 1 5 5 5 5 5 7 0 1 5 5 5 5 6 0 8 // // 2130 1 5 5 5 5 6 B B 1 5 5 5 6 3 5 0 // // 2140 1 5 D 2 F 1 C D 1 5 D 2 F 2 5 C // // 2150 1 5 D 2 F 3 0 F 1 5 D 2 F F A 5 // // 2160 1 C 9 6 E B D 2 1 C 9 6 E C 6 1 // // 2170 1 C 9 6 E D 1 4 1 C 9 6 F 9 A 9 // // Put DDS tuning words into EEPROM #rom 0x2100={0x0A,0xAA,0xAA,0xCE} #rom 0x2108={0x0A,0xAA,0xAB,0x5D} #rom 0x2110={0x0A,0xAA,0xAC,0x10} #rom 0x2118={0x0A,0xAA,0xB8,0xA5} #rom 0x2120={0x15,0x55,0x55,0x79} #rom 0x2128={0x15,0x55,0x56,0x08} #rom 0x2130={0x15,0x55,0x56,0xBB} #rom 0x2138={0x15,0x55,0x63,0x50} #rom 0x2140={0x15,0xD2,0xF1,0xCD} #rom 0x2148={0x15,0xD2,0xF2,0x5C} #rom 0x2150={0x15,0xD2,0xF3,0x0F} #rom 0x2158={0x15,0xD2,0xFF,0xA5} #rom 0x2160={0x1C,0x96,0xEB,0xD2} #rom 0x2168={0x1C,0x96,0xEC,0x61} #rom 0x2170={0x1C,0x96,0xED,0x14} #rom 0x2178={0x1C,0x96,0xF9,0xA9} // Function declarations void send_byte(byte dat); void read_switches(void); #if(0) // Not used byte readhex1(void); byte readhex(void); #endif #if(0) // Not used byte ReadEEPROM(byte address); void WriteEEPROM(byte address, byte value); #endif // Global variables // Variables byte val0; // LS byte of 16 or 32-bit user input byte val1; // MS byte (16-bits) or 2nd LS byte (32-bits) byte val2; // Next 3rd LS byte (for 32 bits) byte val3; // MS byte (for 32 bits) int freq; // Freq switch # (0-15) int32 freq32; // Freq word #if(1) // Main function w/o user interface // Main function void main(void) { // Local variables int i; // Index // Initializations // Turn on LED output_high(LED); // Do DDS load 10 times - more reliable for(i=0; i<10; i++) { // Read DIP switches read_switches(); #if(0) // Use custom EEPROM read function // Read selected freq from EEPROM into 32-bit int val3=ReadEEPROM(0x2100+freq*8); val2=ReadEEPROM(0x2101+freq*8); val1=ReadEEPROM(0x2102+freq*8); val0=ReadEEPROM(0x2103+freq*8); freq32=make32(val3, val2, val1, val0); #endif #if(1) // Use standard EEPROM read function // Read selected freq from EEPROM into 32-bit int val3=read_eeprom(0x2100+freq*8); val2=read_eeprom(0x2101+freq*8); val1=read_eeprom(0x2102+freq*8); val0=read_eeprom(0x2103+freq*8); freq32=make32(val3, val2, val1, val0); #endif // Do DDS master reset // Raise RESET line for 10 usec output_high(RESET); delay_us(10); output_low(RESET); // Do DDS I/O Reset // Raise IOSYNC line for 10 usec output_high(IOSYNC); delay_us(10); output_low(IOSYNC); // Send DDS control info for single tone mode at full amplitude // Control register address for CFR1 = 0x00 // Note: Don't need to send this because default 0x00 is OK // 7 LS bits = register 0 hex // MS bit = R/W = 0 = write // Control Function Register #1 bits: // 31 30 29 28 27 26 25 24 // Not Not Not Not Not Load OSK Auto // Used Used Not Used Used ARR Enable OSK // 0 0 0 0 0 0 0 0 // // 23 22 21 20 19 18 17 16 // Auto Manual Not Not Not Not Not Not // Sync Sync Used Used Used Used Used Used // 0 0 0 0 0 0 0 0 // // 15 14 13 12 11 10 09 08 // Not Not AutoClr Enable Not Clear SDIO LSB // Used Used Phase Sine Used Phase Input First // 0 0 0 0 0 0 0 0 // // 07 06 05 04 03 02 01 00 // Dig Not DAC Clk In Ext Not SyncClk Not // Pwr Dn Used Pwr Dn Pwr Dn Pwr Dn Used Disable Used // 0 0 0 0 0 0 0 0 // Control data address for CFR2 = 0x01 // 7 LS bits = register 1 hex // MS bit = R/W = 0 = write // Send CFR2 address send_byte(0x01); // Control Function Register #2 bits: // 23 22 21 20 19 18 17 16 // Not Not Not Not Not Not Not Not // Used Used Used Used Used Used Used Used // 0 0 0 0 0 0 0 0 // // 15 14 13 12 11 10 09 08 // Not Not Not Not HS Sync ManSync XO Out Not // Used Used Used Used Enable Enable Axtive Used // 0 0 0 0 0 0 0 0 // // 07 06 05 04 03 02 01 00 // RefClk RefClk RefClk RefClk RefClk VCO Charge Charge // Mult Mult Mult Mult Mult Range Pump Pump // 0 1 1 0 0 0 0 0 // Send 3 bytes of CFR2 data send_byte(0x00); send_byte(0x00); send_byte(0x60); // Use default 0x00 for ASF register 0x02 // Use default 0x00 for ARR register 0x03 // Use default 0x00 for POW0 register 0x05 // Write frequency word // Control register address for FTW0 = 0x04 // 7 LS bits = register 4 hex // MS bit = R/W = 0 = write // Send FTW0 address send_byte(0x04); // Send 4 bytes of frequency data send_byte(make8(freq32, 3)); send_byte(make8(freq32, 2)); send_byte(make8(freq32, 1)); send_byte(make8(freq32, 0)); // Send I/O update // Raise IOUPDATE line for 10 usec output_high(IOUPDATE); delay_us(10); output_low(IOUPDATE); // Delay so LED is on longer delay_ms(50); } // Turn off LED after DDS loading output_low(LED); // Go to sleep sleep(); } #endif #if(0) // Draft of main function with user interface - incomplete // Note: This code not updated for AD9951 // Main function void main(void) { // Local variables int key; // RS-232 getc() input character // Initializations // Read DIP switches read_switches(); // Read default freq from EEPROM into 32-bit int val3=ReadEEPROM(0x2100+freq*8); val2=ReadEEPROM(0x2101+freq*8); val1=ReadEEPROM(0x2102+freq*8); val0=ReadEEPROM(0x2103+freq*8); freq32=make32(val3, val2, val1, val0); // Flash LED for 100 msec at startup output_high(LED); delay_ms(100); output_low(LED); // Do DDS master reset // Raise RESET line for 10 usec output_high(RESET); delay_us(10); output_low(RESET); // Send DDS control info for unramped FSK at full amplitude // Do DDS I/O Reset // Raise IOSYNC line for 10 usec output_high(IOSYNC); delay_us(10); output_low(IOSYNC); // Send control register instruction byte // 7 LS bits = register 7 hex // MS bit = R/W = 0 send_byte(0x07); // Send 4 bytes of control data // Control Register bits: // 31 30 29 28 27 26 25 24 // Don't Don't Don't Comp Always Control DAC DIG // Care Care Care PD Low DAC PD PD PD // 0 0 0 1 0 0 0 0 = 10 hex default // 0 0 0 1 0 0 0 0 = 10 hex actual // // 23 22 21 20 19 18 17 16 // Don't Pll Bypass Ref Ref Ref Ref Ref // Care Range PLL Mult 4 Mult 3 Mult 2 Mult 1 Mult 0 // 0 1 1 0 0 1 0 0 = 64 hex default // 0 0 0 0 1 1 0 0 = 0C hex actual // // 15 14 13 12 11 10 09 08 // CLR CLR Tri- Don't Mode Mode Mode Int/Ext // ACC1 ACC2 angle Care 2 1 0 UD Clk // 0 0 0 0 0 0 0 1 = 01 hex default // 0 0 0 0 0 0 0 0 = 00 hex actual // // 07 06 05 04 03 02 01 00 // Don't Bypass OSK OSK Don't Don't LSB SDO // Care InvSinc EN INT Care Care First Active // 0 0 1 0 0 0 0 0 = 20 hex default // 0 1 0 0 0 0 0 0 = 40 hex actual send_byte(0x10); // Defaults send_byte(0x0C); // Ref mult = 12 = 0x0C // PLL Range = 0 // Bypass PLL = 0 send_byte(0x00); // Default except and Ext Update Clock send_byte(0x40); // Default except no inv sinc and OSK EN = 0 // Enable interrupts enable_interrupts(GLOBAL); // Loop forever // Wait for timer interrupt while(TRUE) { // Has RS-232 character been received? if(kbhit()) { // Disable interrupts during RS-232 parsing disable_interrupts(GLOBAL); // Get character from RS-232 user interface key=getc(); // Parse command // F = Enter freq word into freq32 variable // L = Load current freq into DDS // N = Select freq - this overrides switch # // R = Reset PIC - This loads switch-selected freq // S = Save current freq word in EEPROM at current # // V = Get S/W version // W = Get current freq word switch(key) { // Enter freq word case 'F': { // Read 4 bytes as pairs of hex digits // from PC MS byte 1st // This applies to the current freq switch # val3=readhex(); val2=readhex(); val1=readhex(); val0=readhex(); freq32=make32(val3, val2, val1, val0); } break; // Load freq into DDS case 'L': { ; } // Select freq // Get freq choice per switch # (0-15) // as one hex character (0-F) case 'N': { // Read freq # freq=readhex(); } break; // Reset PIC case 'R': { reset_cpu(); } break; // Save current freq in EEPROM // EEPROM address is 0x2100 plus freq switch # case 'S': { // Save freq data WriteEEPROM(0x2100+freq, make8(freq32, 3)); WriteEEPROM(0x2101+freq, make8(freq32, 2)); WriteEEPROM(0x2102+freq, make8(freq32, 1)); } break; // Get S/W version case 'V': { printf("%s", DMTD_REV); } break; // Get freq word case 'W': { printf("%8LX", freq32); } break; // Invalid command default: { // Error printf("E"); } } // Send CR/LF printf("\n\r"); // Reenable interrupts enable_interrupts(GLOBAL); } } } #endif // Functions #if(0) // Not used // Sub-function to read 1 hex character // and convert it to a number byte readhex1(void) { // Local variable char digit; // Get 1 hex character digit=getch(); // Convert it to a nuneric value if(digit<='9') { return(digit-'0'); } else { return((toupper(digit)-'A')+10); } } // Function to read byte as 2 hex digits byte readhex(void) { // Local variables int lo; int hi; // Get numeric values of 2 hex chars hi=readhex1(); lo=readhex1(); // Return composite numeric value return(hi*16+lo); } #endif #if(0) // Not used // Asm code for reading data from EEPROM // Adapted/corrected from Microchip code on p. 93 of PIC16F62X manual // Replaces CCS PCW read_eeprom() function // that does work now but results in 9% larger ROM usage // EEPROM address is function argument // EEPROM data byte is returned byte ReadEEPROM(byte address) { // Local variable byte value; // Restart watchdog timer restart_wdt(); #asm bcf STATUS, RP0 // Select bank 0 movf address, W // Put address into w bsf STATUS, RP0 // Select bank 1 movwf EEADR // Move it into EEADR bsf EECON1, RD // Read eeprom movf EEDATA, W // Put data into w bcf STATUS, RP0 // Select bank 0 movwf value // Move data into value #endasm return value; } // Asm code for writing data to EEPROM // Adapted from Microchip code on p. 93 of PIC16F62X manual // Replaces CCS PCW write_eeprom() function // that doesn't work for this device // EEPROM address is function argument // Note: Interrupts must be disabled before calling this function void WriteEEPROM(byte address, byte value) { // Restart watchdog timer restart_wdt(); #asm bcf STATUS, RP0 // Select bank 0 movf address, W // Put address into w bsf STATUS, RP0 // Select bank 1 movwf EEADR // Move adr into EEADR bcf STATUS, RP0 // Select bank 0 movf value, W // Put value into w bsf STATUS, RP0 // Select bank 1 movwf EEDATA // Move val into EEDATA bsf EECON1, WREN // Enable write movlw 0x55 // Load 55h into w movwf EECON2 // Move it into EECON2 movlw 0xAA // Load AAh into w movwf EECON2 // Move it into EECON2 bsf EECON1, WR // Write eeprom bcf STATUS, RP0 // Select bank 0 loop: // This is a C label btfss PIR1, EEIF // Wait for write done goto loop // EEIF set when done bcf PIR1, EEIF // Clear write done bit #endasm } #endif // Subroutines // Send 1 byte to DDS via serial interface // Data line is SDIO // Clock line is SCLK // Data is sent MSB first (default) // Data is transferred on rising edge of clock // 5 usec clock duration is sufficient void send_byte(byte dat) { // Local variables int i; // Index // Loop through 1 byte of data for(i=0; i<8; i++) { // Set data pin according to data if(bit_test(dat, 7-i)) { output_high(SDIO); } else // Bit is 0 { output_low(SDIO); } // Toggle clock up and down output_high(SCLK); delay_us(5); output_low(SCLK); } } // Read switch states void read_switches(void) { freq=input(SWITCH8)*8+input(SWITCH4)*4+ input(SWITCH2)*2+input(SWITCH1); }