📄 mcu_atmega48_88.c
字号:
/*= mcu_atmega48_88.c ==========================================================
*
* Copyright (C) 2005 Nordic Semiconductor
*
* This file is distributed in the hope that it will be useful, but WITHOUT
* WARRANTY OF ANY KIND.
*
* Author(s): Borge Strand
*
* Description:
*
* MCU specific code for Atmel ATmega48 and ATmega88
* This file must be ported to any other MCU to be used.
* It must implement all functions in mcu.h
* All defines specific to ATmega48/88 reside in this file!
*
* Compiler: Tested with WinAVR, avr-gcc (GCC) 3.4.3
*
* Revision: 2.0
*
*==============================================================================
*/
// The following hardware is used in the ATmega48:
// PB0 Button - active high
// PB1 Button - active high
// PB2 Button - active high (SPI chip select)
// PB3 SPI MOSI - in-system programming
// PB4 SPI MISO - in-system programming
// PB5 SPI SCK - in-system programming
// PB6 XTAL1 - see page 29 of data sheet
// PB7 XTAL2 - see page 29 of data sheet
//
// PC0 NC
// PC1 Output to a LED - active high
// PC2 Random seed bit - connected to I2S bit clock or MCLK
// PC3 Connected to wakeup pin DD[1] of nRF24Z1
// PC4 2-wire SDA - used for both nRF24Z1 and ADC
// PC5 2-wire SCL - used for both nRF24Z1 and ADC
// PC6 Reset - in-system programming
// PC7 Doesn't exist on chip
//
// PD0 UART RXD
// PD1 UART TXD
// PD2 INT0 - connected to IRQ of nRF24Z1
// PD3 NC
// PD4 NC
// PD5 Button - active high
// PD6 Button - active high
// PD7 Button - active high
#include "mcu.h"
// MCU specific includes, change if porting to a different MCU or compiler
#include <stdio.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>
#include <avr/io.h>
// Some forward declarations of functions that are not available externally
void mcu_wait_timer(void);
void mcu_wait_32ms(void);
#ifdef MCU_3686
#define F_CPU 3686000UL // Oscillator on STK500
#endif
#ifdef MCU_8000
#define F_CPU 8000000UL // Internal RC oscillator, no 8x division in fuse bits
#endif
#ifdef MCU_1000
#define F_CPU 1000000UL // Internal RC oscillator, with 8x division in fuse bits (default for atmega48/88)
#endif
// Handler for timer/counter1 interrupt
SIGNAL(SIG_OVERFLOW1) {
// Simply return after interrupt
}
// Handler for watchdog timer interrupt
SIGNAL(SIG_WATCHDOG_TIMEOUT) {
// Simply return after interrupt.
}
// Initialize the MCU. The atmega16 needs no other init than what is done in
// separate functions.
void mcu_init(void) {
DDRC = 0x0A; // PC3 goes to DD[1] of nRF24Z1, PC1 goes to LED
ACSR = 0xF0; // Analog comparator is off
char temp = 0b01100001; // Shut down Timer/Counter2, Timer/Counter0 and ADC
temp |= 0b00000100; // Shut down SPI
#ifndef DEBUG // If no debug is used,
temp |= 0b00000010; // shut down uart
#endif
PRR = temp; // Store power reduction register
mcu_wait_ms(96); // Wait for nRF24Z1 to load firmware and settings
}
// Halt the MCU for a number of 32ms periods using the watchdog timer, low power consumption
// Atmega48/88 goes into power down mode where only watchdog, external interrupts, and 2w address
// decode continue to run. See page 38 of full data sheet. A convenient delay time is 32ms by means
// of watchdog interrupted sleep.
void mcu_wait_32ms(void) { // Lowest power down mode makes uart very unhappy
// Code example to change time-out value of watchdog, page 51 of full data sheet
cli(); // Disable interrupts
wdt_reset(); // Watchdog reset
WDTCSR |= (1<<WDCE) | (1<<WDE); // Enable changes in WDTCSR
WDTCSR = (1<<WDIE) |(1<<WDP0); // 4k cycles (~32ms), enable watchdog interrupts
#ifdef DEBUG // PWR_DOWN kills UART while IDLE saves it.
set_sleep_mode(SLEEP_MODE_IDLE); // Low power sleep that saves UART
#else
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Lowest power sleep that kills UART, saves 0.3mA
#endif
sei(); // Enable interrutps
sleep_mode(); // Wait until interrupt
// Code example to turn off watchdog, page 50 of full data sheet
cli(); // Disable interrupts
wdt_reset(); // Watchdog reset
MCUSR &= ~(1<<WDRF); // Set WDRF bit in MCUCR to 0 to clear watchdog interrupt flag, needed?
WDTCSR |= (1<<WDCE) | (1<<WDE); // Enable changes in WDTCSR
WDTCSR = 0x00; // Turn off watchdog timer
sei(); // Turn on interrupts again
}
// Halt the MCU for a number of 1ms periods by means of low-power and high-power sleeps
void mcu_wait_ms(unsigned int time) {
extern char wakeup;
wakeup = WAKE_WAIT; // Assume that the delay is going to wake up the MCU
while ((time > 32) && (wakeup == WAKE_WAIT)) { // Wait for set time, or until another interrupt arrives
mcu_wait_32ms(); // Use low-power sleep for anything above 32ms
time -= 32; // Prepare to wait some more
}
while ((time > 0) && (wakeup == WAKE_WAIT)) { // Wait until set time has passed, or until another interrupt arrives
TCNT1 = -((uint16_t)(1e-3 * F_CPU)); // Sleep 1ms. Max time at 3.686MHz is 65535 / 3686000 = 17ms
mcu_wait_timer(); // Do the actual sleep for 1ms
time--; // Prepare to wait a little more
}
}
// Go in to sleep mode after the calling function has set the desired delay
// The only allowable interrupt source is timer interrupt!
void mcu_wait_timer(void) {
cli(); // Go for atomic operations while setting up the interrupt source
TIMSK1 = (1<<TOIE1); // Enable t/c 1 overflow int
TCCR1A = 0; // t/c 1 normal mode
TCCR1B = (1<<CS10); // Start timer 1 with prescaler 1
set_sleep_mode(SLEEP_MODE_IDLE); // Idle mode keeps counters running
sei(); // Set global interrupt flag
sleep_mode(); // Wait until interrupted by pin or counter
cli(); // Clear global interrupt flag
TIMSK1 = 0x00; // Disable all timer/counter interrupts
TCCR1B = 0x00; // Stop timer 1
sei(); // Turn on interrupts again
}
// Produce a pseudo random byte by looking at a signal that is presumed to be asynchronous to the MCU clock
// Improve to flatten histogram of output!
char mcu_randombyte(void) {
// extern char randombyte; // A number different from 0x00 to start the LFSR
static char randombyte = 0xAA;
char readbyte = 0;
char count = 0;
// Simulate 7-bit linear feedback shift register x^7 + x^4 + input
for (count=81; count!=0; count--) { // Number of xors to perform. Time vs. enthropy trade off
if ((PINC & 0x04) != 0x00) // PC2 is random input from I2C bitclock, async. with MCU clock
readbyte = 1;
else
readbyte = 0;
if ((randombyte & 0x40) != 0x00) // XORing x^7 (MSB has index 8 in LFSRs!!)
readbyte = ~readbyte;
if ((randombyte & 0x08) != 0x00) // XORing x^4
readbyte = ~readbyte;
readbyte &= 0x01; // Only keep LSB of readbyte
randombyte <<=1; // Left shift result
randombyte |= readbyte; // ORing in a new LSB
}
return randombyte;
}
// Set up MCU's master 2-wire interface
void mcu_2w_master_init(void) {
#ifdef MCU_3686
TWBR = 0x0C; // SCL speed at 92.5kbps, not tried with ADC
#endif
#ifdef MCU_8000
TWBR = 0x24; // SCL speed at 91kbps, works with nRF24Z1, doesn't work with ADC
#endif
#ifdef MCU_1000
TWBR = 0x05; // Lower SCL speed suitable with low-resolution clock division, works with nRF24Z1 and ADC
#endif
TWSR = 0; // See mega48_88 datasheet p. 215
TWAR = 0xAA; // 2-w adr. of this unit is 1010101, no answer to general call
TWCR = (1<<TWEN); // Enable two-wire interface
TWDR = 0xFF; // Default content = SDA released.
}
// Read data from 2-wire slave interface of nRF24Z1, this is all MCU specific!
// DOC: Interrupts must be off during this function!
char mcu_2w_read(char devadr, char adr) {
char failure = 0; // No errors have been detected this far
char temp = 0; // Temporary data container
// Brief explanation of nRF24Z1 2-wire read:
// Send start condition
// Send device address with r/_w = 0
// Send internal register address
// Send new start condition
// Send device address with r/_w = 1
// Read byte, NACK when done
// 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 and _w bit failed
}
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) {
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // Send another start condition
loop_until_bit_is_set(TWCR,TWINT); // Wait until the start condition has been sent
if ((TWSR & 0xF8) != 0x10) // Test for valid restart condition sent
failure = 4; // Restart condition failed
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -