📄 main.c
字号:
/* * main.c -- USBLiveOsci clock generator. * * Copyright (c) 2006 by Wolfgang WIESER <wwieser@gmx.de> * */#include <inttypes.h>#include <avr/signal.h>#include <avr/io.h>#include <avr/wdt.h>#include <avr/interrupt.h>#include "../defines.h"#define nop() __asm__ __volatile__("nop")typedef signed char int8;typedef uint8_t uint8;typedef uint16_t uint16;typedef uint32_t uint32;// This should be in attiny2313 headers: #define TCCR0A _SFR_IO8(0x30)#define TCCR0B _SFR_IO8(0x33)#define OCR0A _SFR_IO8(0x36)#define TIMSK _SFR_IO8(0x39)#define TCNT0 _SFR_IO8(0x32)#define SIG_INTERRUPT0 _VECTOR(1)#define SIG_INTERRUPT1 _VECTOR(2)#define SIG_OVERFLOW0 _VECTOR(6)#define SIG_OUTPUT_COMPARE0A _VECTOR(0xd)#define SCK_LEVEL() (PIND & (1U<<4)) /* NOTE: Result is 0 or !=0. */#define RXD_LEVEL() (PIND & (1U<<0)) /* NOTE: Result is 0 or !=0. */#define SET_TXD_HIGH() sbi(PORTD,1)#define SET_TXD_LOW() cbi(PORTD,1)// PD3 is INT1. Result is 0 or !=0. HIGH=sampling, LOW=IO. #define INT1_LEVEL() (PIND & (1U<<3))// Note: To disrespect INT1 while still producing the correct timings, // we should use another pin like (PIND & (1U<<5)) [unused] and // set it up properly. //#define INT1_LEVEL() (1) /* Ignore pin; always sample. *///==============================================================================// ALL GLOBAL VARS GO HERE, BEFORE THE FIRST FUNCTION DEF. //==============================================================================// Clock speed value in multiples of 50kS/s. // Only certain discrete values are allowed; see ClockGeneratorLoop(). volatile uint8 clock_speed=200; // 200 -> 10MS/s; 50 -> 2.5MS/s// For external interrupt 0: SIGNAL(SIG_INTERRUPT0){ // Not needed. Should never trigger. }// For external interrupt 1: SIGNAL(SIG_INTERRUPT1){ // Not needed. Should never trigger. }SIGNAL(SIG_OVERFLOW0){ /*cbi(PORTB,3); sbi(PORTB,3);*/}SIGNAL(SIG_OUTPUT_COMPARE0A){ /*cbi(PORTB,3); sbi(PORTB,3);*/}static void Initialize(){ // Disable watchdog timer (be sure). wdt_disable(); // Disable analog comparator (to be sure). sbi(ACSR,7); // ----- IO SETUP ----- // PB2,3 are outputs. sbi(PORTB,2); sbi(DDRB,2); // PB2: clock1, high -> "don't sample" cbi(PORTB,3); sbi(DDRB,3); // PB3: clock0, low // PD0,2,3,4 is input with pullup. cbi(DDRD,0); sbi(PORTD,0); cbi(DDRD,2); sbi(PORTD,2); cbi(DDRD,3); sbi(PORTD,3); cbi(DDRD,4); sbi(PORTD,4); // PD1, PD6 are outputs. cbi(PORTD,1); sbi(DDRD,1); // PD1: TxD, low sbi(PORTD,6); sbi(DDRD,6); // PD6: aux, high // Set up INT1 (PD3) as edge triggered: H -> L (negative edge): 0000 1000 //MCUCR=0x08U; // Enable INT1: //sbi(GIMSK,7);}// We need to sync on the main clock. // The previous method assigning TCNT1 will not work because this can // insert a tiny clock gap for the main clock messing up the FX2 (IMO). // At least it took me some hours to figure out that the assignment to // TCNT1 was the reason for the USB traffic to stall mysteriously although // the relative timing of the write strobes was correct. // Polling TCNT1 instead of the assignment did not work properly for me. // So, the new method uses a loop and polls the output pin directly. // The polling loop must have an odd number of instructions to ensure that // we see different states of the pin each time. (I verified that even // numbers WILL cause hangs!) // Okay, this is not the smartest method but it is implemented quickly and // since we will never have more than 2 iterations in the loop, execution // speed does not matter as well. // NOTE: SampleClockSync_ASM will clobber r24. #define SampleClockSync_ASM \ "in r24, 0x16" "\n" /* (1) Read PINB into r24. */ \ " andi r24, 0x08" "\n" /* (1) Check if bit 3 is set. */ \ " nop" "\n" /* (1) To make clock count odd. */ \ " breq .-8" "\n" /* (1/2) 2 clocks in loop -> 4 */// Tiny delay loop. Will make "count"-many iterations. // Each iteration makes 4 cycles; setup is compensated by last iteration. // Hence, total number of cycles is 4*count. // NOTE: Since this is ASM, insert count as string literal. // NOTE: Will clobber r24. #define DelayLoop_ASM(count) \ " ldi r24, " count "-1" "\n"/* (1) */ \ " nop" "\n" /* (1) */ \ " subi r24, 0x01" "\n" /* (1) */ \ " brcc .-6" "\n" /* (2), (1 for last, compensated by first) */// Three clock cycles (while looping) end-of-loop conditional jump statement. // Must pass label name as string. #define SampleClockLoopEnd_ASM(label) \ " sbic 0x10, 3" "\n" /* (1) */ \ " rjmp " label "\n" /* (2) */// Must pass label and delay as strings. // Total cycle time is 4*delay + 8. #define SampleClockLoopWithDelay(label,delay) \ __asm__ __volatile__( \ SampleClockSync_ASM /* Sync on clock; will clobber r24 */ \ label ":" "\n" /* Loop start label. */ \ " cbi 0x18, 2" "\n" /* (2) cbi(PORTB,2); */ \ " sbi 0x18, 2" "\n" /* (2) sbi(PORTB,2); */ \ " nop" "\n" /* (1) nop(); */ \ DelayLoop_ASM(delay) /* (4*delay) delay; clobbers r24 */ \ SampleClockLoopEnd_ASM(label) /* (3) */ \ : /* output operands */ /* SUM: 4*delay+8 cycles */ \ : /* input operands */ \ : "r24" /* clobbered registers */ )// This will set up the main sample timer at 10MHz. // The 50% duty cycle waveform is available on pin PB3 (clock0). // There is no reason to ever stop the sampling clock; we just run the // AD converter at this speed and don't need to actually store the samples. static void StartSampleClock(){ // clock0 (PB3): Set up timer0 in CTC mode: // Prescaler to 1: // wgm1 3:0 = 0100 OCR1A=0x0; // 0x00 TCNT1=0; TCCR1A=0x40U; // 01000000 <-- COM1A1:0, WGM11:0 TCCR1B=0x09U; // 00001001 <-- WGM13:2, CS12:0 TCNT1=0;}// NOTE: PIND:3 is HIGH while sampling and LOW during communication. // NOTE: The write strobe must be LOW during a RISING edge of the sampling // clock. No inverters are in the signal lines. static void ClockGeneratorLoop(){ // Main loop. This runs until PD3 is pulled LOW thereby stopping sampling. if(!INT1_LEVEL()) return; switch(clock_speed) { case 0: // 0kS // This will store NO samples at all. sbi(PORTB,2); // Already HIGH. // Wait for INT1 to go LOW. while(INT1_LEVEL()); break; case 1: // 50kS // This will store every 200th sample: (50 kS) // Cycle sum: 4*98+8 = 400 (OK). SampleClockLoopWithDelay("sl50","0x62"); break; case 2: // 100kS // This will store every 100th sample: (100 kS) // Cycle sum: 4*48+8 = 200 (OK). SampleClockLoopWithDelay("sl100","0x30"); break; case 5: // 250kS // This will store every 40th sample: (250 kS) // Cycle sum: 4*18+8 = 80 (OK). SampleClockLoopWithDelay("sl250","0x12"); break; case 10: // 500kS // This will store every 20th sample: (500 kS) // Cycle sum: 4*8+8 = 40 (OK). SampleClockLoopWithDelay("sl500","0x08"); break; case 20: // 1MS // This will store every 10th sample: (1 MS) // Cycle sum: 4*3+8 = 20 (OK). SampleClockLoopWithDelay("sl1000","0x03"); break; case 50: // 2.5MS // This will store every 4th sample: (2.5 MS) __asm__ __volatile__( \ SampleClockSync_ASM /* Sync on clock; will clobber r24 */ \ "sl2500:" "\n" /* Loop start label. */ \ " cbi 0x18, 2" "\n" /* (2) cbi(PORTB,2); */ \ " sbi 0x18, 2" "\n" /* (2) sbi(PORTB,2); */ \ " nop" "\n" /* (1) nop(); */ \ SampleClockLoopEnd_ASM("sl2500") /* (3) */ \ : /* output operands */ /* SUM: 8 cycles (OK) */ \ : /* input operands */ \ : "r24" /* clobbered registers */ ); break; case 100: // 5MS // This will store every 2nd sample: (5 MS) // clock1 (PB2): Set up timer0 in CTC mode. Prescaler to 1: // TCCR0A: COM0A1 COM0A0 COM0B1 COM0B0 0 0 WGM01 WGM00 // 0 1 0 0 0 0 1 0 // TCCR0B: FOC0A FOC0B 0 0 WGM02 CS02 CS01 CS00 // 0 0 0 0 0 0 0 1 // Set: COM0A1=0, COMA0=1 -> toggle on compare match // WGM02=0, WGM01=1, WGM00=0 -> CTC mode // CS02=0, CS01=0, CS00=1 -> system clock OCR0A=0x01; // "TOP" value. // FUCK IT!! I was the hell not able to find out how to gracefully // start this fucking wave without at least one undesired // spike at the beginning. I should be possible, though. // So, my solution is to temporarily switch off the port. GRRR!!! //sbi(PORTB,2); // (already) cbi(DDRB,2); // <-- Switch off port (pull-up still active). TCNT0=0x01; // Counter register. TCCR0A=0x42U|0x80U; // 11000010 (set on compare match) TCCR0B=0xf0U; // Force a compare. sbi(DDRB,2); // <-- Switch on port. TCNT0=0xff; // Counter register. TCCR0A=0x42U; // 01000010 <-- COM0A1:0, WGM00:1 // NOTE: The clock source is still switched off; this is done // by setting the lowest bit in TCCR0B, but synchronized! //TCCR0B=0x01U; // 00000001 __asm__ __volatile__( \ SampleClockSync_ASM /* Sync on clock; will clobber r24 */ \ "nop" "\n" /* (1) */ \ "ldi r24, 0x01" "\n" /* (1) */ \ "out 0x33, r24" "\n" /* (1) TCCR0B */ \ : /* output operands */ \ : /* input operands */ \ : "r24" /* clobbered registers */ ); while(INT1_LEVEL()); break; case 200: // 10MS // This will store all samples: (10 MS) __asm__ __volatile__( \ SampleClockSync_ASM /* Sync on clock; will clobber r24 */ \ "cbi 0x18, 2" "\n" /* (2) cbi(PORTB,2); */ \ : /* output operands */ \ : /* input operands */ \ : "r24" /* clobbered registers */ ); // Sample until INT1 goes LOW. while(INT1_LEVEL()); break; default: // This is an error. // Don't sample: sbi(PORTB,2); // Already HIGH but be sure. // Wait for INT1 to go LOW. while(INT1_LEVEL()); break; } // Stop recording: This works for sampling modes based on internal counter. TCCR0B=0x0U; TCCR0A=0x4U; OCR0A=0x0; // No recording: This works for all sampling modes not using the // internal counter. sbi(PORTB,2);}// Returns the read byte in the lower 8 bit and status in the higher 8 bit. // Status is 0 for success and 1 if stopped by PD3=HIGH. // Will write back the passed value during transfer. static uint16 SerialIOByte(uint8 to_send)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -