📄 mcu_atmega48_88.c
字号:
if (failure==0) {
TWDR = (devadr<<1) | 0x01; // 7-bit device address, LSB=1 for read
TWCR = (1<<TWINT) | (1<<TWEN); // Start transmission of address
loop_until_bit_is_set(TWCR,TWINT); // Wait until the device address has been sent
if ((TWSR & 0xF8) != 0x40) // Test for ACK after device address and r bit
failure = 5; // Device address and r bitfailed
}
if (failure==0) {
TWCR = (1<<TWINT) | (1<<TWEN); // Initiate reception
loop_until_bit_is_set(TWCR,TWINT); // Wait until data has been received
if ((TWSR & 0xF8) != 0x58) // Check reception of last byte
failure = 6; // Last byte receptioin failed
temp = TWDR; // Read the actual data
}
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); // Send stop condition regardless of failure
if (failure != 0) {
#ifdef DEBUG2W // If failure,
mcu_putchar('R');
mcu_putchar('[');
db_puthex(failure);
mcu_putchar(']');
mcu_putchar('(');
db_puthex(adr);
mcu_putchar(')');
db_putenter();
#endif // DEBUG2W
}
return temp; // Return read data
} // mcu_2wread
// Write data to 2-wire slave interface of nRF24Z1, this is all MCU specific!
// DOC: Interrupts must be off during this function!
void mcu_2w_write(char devadr, char adr, char data) {
char failure = 0; // No errors have been detected this far
// Brief explanation of nRF24Z1 2-wire write:
// Send start condition
// Send device address with r/_w = 0
// Send internal register address
// Send data byte
// Send stop condition
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // Send start condition
loop_until_bit_is_set(TWCR,TWINT); // Wait until the start condition has been sent
if ((TWSR & 0xF8) != 0x08) // Test for valid start condition
failure = 1; // Start condition failed
if (failure==0) {
TWDR = (devadr<<1) & 0xFE; // 7-bit device address, LSB=0 for write
TWCR = (1<<TWINT) | (1<<TWEN); // Start transmission of address
loop_until_bit_is_set(TWCR,TWINT); // Wait until the device address has been sent
if ((TWSR & 0xF8) != 0x18) // Test for ACK after device address and _w bit
failure = 2; // Device address with _w bitfailed
}
if (failure==0) {
TWDR = adr; // Send internal address of nRF24Z1
TWCR = (1<<TWINT) | (1<<TWEN); // Start transmission of address
loop_until_bit_is_set(TWCR,TWINT); // Wait until the internal address has been sent
if ((TWSR & 0xF8) != 0x28) // Test for ACK after data = internal address
failure = 3; // Internal address failed
}
if (failure==0) {
TWDR = data; // Write the actual data
TWCR = (1<<TWINT) | (1<<TWEN); // Initiate reception
loop_until_bit_is_set(TWCR,TWINT); // Wait until data has been received
if ((TWSR & 0xF8) != 0x28) // Test for ACK after data transfer
failure = 4; // Data transmission failed
}
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); // Send stop condition regardless of failure
if (failure != 0) {
#ifdef DEBUG2W // If failure, display debug information
mcu_putchar('W');
mcu_putchar('[');
db_puthex(failure);
mcu_putchar(']');
mcu_putchar('(');
db_puthex(adr);
mcu_putchar(')');
db_putenter();
#endif // DEBUG2W
}
} // mcu_2wwrite
#ifdef DEBUG
// Sends a single char over the uart
int mcu_putchar(char c) {
loop_until_bit_is_set(UCSR0A,UDRE0); // Wait until the send register is empty
UDR0 = c; // Send char to send register
return 0;
}
// Halts, then receives a single char from the uart. Immediate echo
int mcu_getchar(void) {
char c;
loop_until_bit_is_set(UCSR0A,RXC0); // Wait untill a char is received
c = UDR0; // Read char from uart
mcu_putchar (c); // Echo the character
return c;
}
// Sets up the uart 8 data, 1 stop
void mcu_uart_init(void) {
UBRR0H = 0x00; // This register is always 0x00.
#ifdef MCU_3686 // With 3.686MHz,
UBRR0L = 3; // Set up for 57600 baud
#endif
#ifdef MCU_8000 // With 8MHz,
UBRR0L = 12; // Set up for 38400 baud
#endif
#ifdef MCU_1000 // With 1MHz,
UBRR0L = 12; // Set up for 4800 baud
#endif
UCSR0B = (1<<RXEN0)|(1<<TXEN0); // Enable both read and write
UCSR0C = (3<<UCSZ00); // 8 bits data, 1 stop bit
}
#endif // DEBUG
// Read hardware interface and report which button is pressed according to standard table
// in hpref_defines.h. You only need to support the buttons that are actually used in your application!
// Rewrite for your hardware. Return values are standard button codes
// from hpref_defines.h
char mcu_buttons(void) {
#ifdef ATXDEVBOARD // Code running on STK500 with active low buttons
if ((PIND&0x80) == 0x00) // Check button at PD7
return BTN_PLAY;
else if ((PIND&0x40) == 0x00) // Check button at PD6
return BTN_VOLDN;
else if ((PIND&0x20) == 0x00) // Check button at PD5
return BTN_VOLUP;
else if ((PINB&0x04) == 0x00) // Check button at PB2
return BTN_SKIPB;
else if ((PINB&0x02) == 0x00) // Check button at PB1
return BTN_SKIPF;
else if ((PINB&0x01) == 0x00) // Check button at PB0
return BTN_STOP;
else
return BTN_NOKEY;
#else // Code running on ATX Headphone Reference Design with active high buttons
if ((PIND&0x80) != 0x00) // Check button at PD7
return BTN_PLAY;
else if ((PIND&0x40) != 0x00) // Check button at PD6
return BTN_VOLDN;
else if ((PIND&0x20) != 0x00) // Check button at PD5
return BTN_VOLUP;
else if ((PINB&0x04) != 0x00) // Check button at PB2
return BTN_SKIPB;
else if ((PINB&0x02) != 0x00) // Check button at PB1
return BTN_SKIPF;
else if ((PINB&0x01) != 0x00) // Check button at PB0
return BTN_STOP;
else
return BTN_NOKEY;
#endif
}
#ifdef USELED
// Turn a LED on the ATX on or off. With atmega48 and 88, the LED is located on PC1.
// On STK500, the LED is active low, reference design has active high LED.
void mcu_atxled(char mode) {
#ifdef ATXDEVBOARD // Code running on STK500 with active low LED
if (mode == LEDON)
PORTC &= ~0x02; // Set bit 1 of PORTC to 0, active low
else if (mode == LEDOFF)
PORTC |= 0x02; // Set bit 1 of PORTC to 1, active low
#else // Code running on headphone reference board with active high LED
if (mode == LEDON)
PORTC |= 0x02; // Set bit 1 of PORTC to 1, active high
else if (mode == LEDOFF)
PORTC &= ~0x02; // Set bit 1 of PORTC to 0, active high
#endif
}
#endif
#ifdef Z1INTERRUPT
// Enable external interrupts from nRF24Z1, active low interrupt on INT0 on PD2
// The MCU pin receiving the interrupt from nRF24Z1 must be edge sensitive. The nRF24Z1 IRQ
// pin may unknown until 0x7F is written to INTSTA. That sets the pin high, and
// a new interrupt may arrive on the edge. Up to one interrupt from the nRF24Z1 may be
// received between calling mcu_z1int_enable() and mcu_z1int_disable();
void mcu_z1int_enable(void) {
cli(); // Setting up interrupts in atomic operation
EIFR |= 0x01; // Clear interrupt flag before looking for new one datasheet p. 66
EICRA |= 0x02; // Int on falling edge of INT0 datasheet p. 65
EIMSK |= 0x01; // Enable INT0 on bit 0 of EIMNSK datasheet p. 66
sei(); // Enable interrupts thereafter
}
// Disable external interrupts from nRF24Z1, active low interrupt on INT0 on PD2
void mcu_z1int_disable(void) {
cli(); // Atomic operation
EIMSK &= 0xFE; // Disable INT0 ONLY
sei(); // End of atomic operation
}
// Wait until IRQ line from nRF24Z1 goes high. This is important, as the IRQ line may go low when nRF24Z1
// has its interrupts enabled. Does not work with power on interrupt!
// Dangerous to have a loop that might turn infinite if nRF24Z1 dies!
void mcu_z1int_clear(void) {
char temp = 0;
while (temp == 0) { // Once, and then While INT0 is active:
z1_singlewrite(INTSTA, 0x7F); // Clear all interrupt flags in nRF24Z1 to make INT0 inactive
mcu_wait_ms(2);
temp = (PIND & 0x04); // Check status of INT0 pin
}
}
// The handler for the nRF24Z1 interrupt does not clear the nRF24Z1 internal interrupt flag! That means
// new nRF24Z1 interrupts may not arrive before the MCU code explicitly enables them. This handler is
// written for programs that sleep until interrupted. Not for programs where normal execution is
// interrupted.
SIGNAL(SIG_INTERRUPT0) {
extern char wakeup;
#ifdef DEBUG
if (wakeup == WAKE_WAIT) // Assume that the delay is going to wake up the MCU
mcu_putchar('w'); // Indicate that the MCU woke up from a controlled sleep-until-int-or-timeout
else
mcu_putchar('z'); // Indicate that MCU woke up from unexpected interrupt
#endif
wakeup = WAKE_Z1_INT; // Woken by an external interrupt from nRF24Z1
}
// Check whether or not the nRF24Z1 is currently outputting an interrupt
char mcu_z1int_active(void) {
if ((PIND & 0x04) == 0x00) // INT0 is active low
return 1; // If the ATX nRF24Z1 is interrupting the MCU, return a true
else
return 0; // If it is not, return a false
}
#endif // Z1INTERRUPT
// Toggle the Z1 ATX wakeup pin on DD[1]. On STK500, this functhion has been assigned to PC3
// mcu_init() must define this pin as an output.
void mcu_z1wakeup_pin(void) {
PORTC |= 0x08; // Set the wakeup pin
mcu_wait_ms(5); // Give ATX time to wake up!
PORTC &= ~0x08; // Clear the wakeup pin
mcu_wait_ms(10); // Wait for ATX nRF24Z1 to be ready to accept commands. In some instances this wait will
} // be terminated by an interrupt (if enabled) resulting from the ATX powering up
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -