The project as follows: an attiny25 powers up a dht22 and an unltrasonic distance sensor with a MOSFET. The output of the MOSFET is also used to power a bus, there's a 3.3K pullup on it. The main program mostly sleeps, and when it's time, it executes this:
if ((buff[1]<<8) + buff[0] <= powerCutoff) {
generalStatus &= wipeMask; // Wipe lower 3 bits
setOutput(signalPin);
setLow(signalPin);
MOSFETPowerOn(devPower)
initSleep(1<<WDP2);
measureDHT();
measureDistance();
// sendData();
MOSFETPowerOff(devPower)
}
You also need to know these:
#define MOSFETPowerOn(line) { line ## _port &= ~(1<<line); line ## _ddr |= (1<<line); asm("nop"); }
#define MOSFETPowerOff(line) { line ## _ddr &= ~(1<<line); line ## _port |= (1<<line); asm("nop"); }
#define ping(bit) { \
bit ## _port &= ~(1<<bit); \
bit ## _ddr |= (1<<bit); \
bit ## _port |= (1<<bit); \
asm("nop"); \
bit ## _port &= ~(1<<bit); \
asm("nop"); }
/** Devices power pin */
#define devPower PB2
#define devPower_port PORTB
#define devPower_ddr DDRB
#define devPower_pin PINB
/** Debug */
#define debug PB0
#define debug_port PORTB
#define debug_ddr DDRB
#define debug_pin PINB
void initSleep(uint8_t timing) {
asm("WDR");
WDTCR = 1<<WDIE | timing;
MCUCR &= ~(1<<SM0);
MCUCR |= 1<<SE | 1<<SM1;
asm("sleep");
}
void measureDHT() {
generalStatus |= 1<<fDHTMeasurementInProgress;
initSleep(1<<WDP2); // 0.25s
for (uint8_t i = 2; i < 7; i++) buff[i] = 0;
dht22_status.status = dht22_inPresence;
dht22_status.bitCounter = 0;
dht22_status.byteCounter = 0;
setOutput(devBus);
_delay_us(1200); // 1ms initial low pulse
setInput(devBus);
_delay_us(50);
...
Clock is 8MHz. The power comes from a regulator, yes there is a small cap. I use parts of this code for many other projects, the initSleep() for example, and it works fine.
The problem is what happens between turning on the power for the dht and initiating the comms with it. As you can see in the datasheet, 1<<WDP2 fot the watchdog timer gives 0.25s sleep time. Then the measureDHT() kicks in and (almost) starts with the same 0.25s sleep, then I drive the bus low. So between power on and driving the bus low, one would expect a bit more than 500ms. Reality is 2349ms. :( The rest of the timing is fine. First row is power, second row is the data from the dht, third row is the distance.
If I add one line after turning on the MOSFET like this:
...
setOutput(signalPin);
setLow(signalPin);
MOSFETPowerOn(devPower)
ping(debug)
initSleep(1<<WDP2);
measureDHT();
...
...this happens:
In the bottom line you see the "ping" as a little spike. The timing is suddenly correct. I ran out of ideas what it could be. I'm using Linux, my avr-gcc is v5.4.0, the programmer is an AVR Dragon. Any ideas?
Edit:
If I reduce the code to:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>
#include "macros.inc"
#include "pinConfig.h"
#define wakeCycle GPIOR0
#define MOSFETPowerOn(line) { line ## _port &= ~(1<<line); line ## _ddr |= (1<<line); asm("nop"); }
#define MOSFETPowerOff(line) { line ## _ddr &= ~(1<<line); line ## _port |= (1<<line); asm("nop"); }
void initSleep(uint8_t timing) {
asm("WDR");
WDTCR = 1<<WDIE | timing;
MCUCR &= ~(1<<SM0);
MCUCR |= 1<<SE | 1<<SM1;
asm("sleep");
}
// Watchdog timeout ISR
ISR(WDT_vect) {
wakeCycle = 12;
}
void measureDHT() {
initSleep(1<<WDP2); // 0.25s
setOutput(devBus);
_delay_us(1200); // 1ms initial low pulse
setInput(devBus);
_delay_us(50);
}
int main(void) {
MOSFETPowerOff(devPower) // MOSFET driven device turned off by pullup resistor
GIMSK |= 1<<PCIE; // Enable pin change interrupt
sei();
while (1) {
MOSFETPowerOn(devPower)
initSleep(1<<WDP2);
measureDHT();
MOSFETPowerOff(devPower)
initSleep(1<<WDP2 | 1<<WDP1 | 1<<WDP0); // 2 sec
}
}
the problem is still the same. If I change the watchdog interrupr to this:
// Watchdog timeout ISR
ISR(WDT_vect) {
asm("nop");
}
it works fine. I remove the DHT from the circuit both this reduced version and the original code work fine. So what kind of craziness is at work here? Is this an electronic or a coding/compiling issue?
Related
This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 4 years ago.
I'm not really that familiar with C so please bear with me...
I'm writing some simple code for a watchdog timer on an Atmel Tiny 85, developing in AtmelStudio. I want to access the clock() function of the time.h library:
#include <time.h>
.....
void main() {
clock_t start = clock();
....
}
Unfortunately the compiler complains that clock is an undefined reference. Every code example I have looked up on the web seems to be doing what I am doing. Is there some fundamental thing I am missing? Thanks!
Here is the full code:
#define F_CPU 1000000UL // 1 MHz
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <time.h>
#include <stdio.h>
#include <avr/wdt.h>
#include <stdint.h>
#include <util/atomic.h>
#define LED_PORT PB1
#define RST0_PORT PINB3
#define RST1_PORT PINB4
#define HEARTBEAT PINB0
volatile int HEARTBEAT_RECEIVED;
#define CLOCKS_PER_SECOND 1000000;
const int TIMEOUT_PERIOD = 120; //reset raspberry pi after this many seconds
void set_output();
void set_input();
void set_interrupts();
void run_timer(clock_t start);
void restart_raspberry_pi();
int main(void)
{
clock_t start;
HEARTBEAT_RECEIVED = false;
//Configure IO pins
set_output();
set_input();
PORTB &= ~(1 << RST0_PORT); // Set pin low (common ground pin for pi reset)
PORTB &= ~(1 << RST1_PORT); // Set pin low (initialize for no pi reset)
//Configure and enable interrupts
set_interrupts();
while (1) {
sei();
start = clock();
run_timer(start);
}
return (0);
}
void run_timer(clock_t start) {
double time_elapsed;
do {
_delay_ms(1000);
if (HEARTBEAT_RECEIVED) { //If heartbeat detected, reset timer.
HEARTBEAT_RECEIVED = false;
return;
}
time_elapsed = ( clock() - start ) / CLOCKS_PER_SECOND;
} while (time_elapsed < TIMEOUT_PERIOD);
restart_raspberry_pi(); //Timeout period has elapsed, reset the pi
}
ISR(PCINT0_vect) {
//Indicate that a heartbeat has been received
HEARTBEAT_RECEIVED = true;
}
void restart_raspberry_pi() {
cli();
PORTB |= (1 << RST1_PORT); // Set pin high (sets RUN high to reset)
_delay_ms(500);
PORTB &= ~(1 << RST1_PORT); // Set pin low (release reset control)
}
void set_output()
{
//The DDxn bit in the DDRx Register selects the direction of this pin.
//If DDxn is written logic one, Pxn is configured as an output pin.
//If DDxn is written logic zero, Pxn is configured as an input pin.
//PORTB = (0<<PB0) | (1<<PB3);
DDRB = (1<<DDB5) | (1<<DDB4) | (1<<DDB3) | (1<<DDB2) | (1<<DDB1); // Set pins as output.
}
void set_input()
{
DDRB |= (0<<DDB0); // set pin 0 as input. Added |=.
}
void set_interrupts()
{
GIMSK |= (1 << PCIE); // pin change interrupt enable
PCMSK |= (1 << PCINT0); // pin change interrupt enabled for PCINT0
}
#Shawn had it right....I dove into the time.h library code available to me and apparently:
Section 7.23.2.1 clock()
The type clock_t, the macro CLOCKS_PER_SEC, and the function clock() are not implemented. We
consider these items belong to operating system code, or to application code when no operating
system is present.
So I guess that's a lesson for me. Thanks for the help.
I am trying to get readings from 3 rotary encoders (KY-040) and send values via UART.
I am using Arduino-Mega 2560 board but due to requirements reason I am programming it in C.
But when I try to get the reading from encoder I get random numbers.
And it only works with every even number of rotation and program gets stuck at odd rotation. (it seems little odd)
Can anybody please suggest what is wrong with my code.
P.S. I am new working with micro controller.
#define F_CPU 16000000 //Clock Speed
#define UART_BAUD 9600
#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "uart.h"
#include <stdlib.h>
volatile unsigned int encPosZ=0;
void sendEncValue(unsigned int value){
char string[5];
itoa(value, string, 10);
uart_puts(string);
}
// main
int main(void)
{
//disable all interrupts
cli();
uart_init(UART_BAUD_SELECT(UART_BAUD,F_CPU));
DDRE &=~ (1 << PE4);
DDRE &=~ (1 << PE5);
/* set pull-up enabled */
PORTE |= (1 << PE4)|(1 << PE5);
EIMSK |= (1 << INT4)|(1 << INT5);
/* INT4 - falling edge, INT5 - rising edge */
EICRB|= (1<<ISC41)|(1<<ISC51)|(1<<ISC50);
// Enable the Global Interrupt Enable flag
sei();
uart_puts("Started... ");
while(1)
{
_delay_ms(5);
}
return 0;
}
//INT4 interrupt
ISR(INT4_vect)
{
if(!bit_is_clear(PINE, PE5)){
encPosZ++;
}else{
encPosZ--;
}
sendEncValue(encPosZ);
}
//INT5 interrupt
ISR(INT5_vect)
{
if(bit_is_clear(PINE, PE4)){
encPosZ++;
}else{
encPosZ--;
}
sendEncValue(encPosZ);
}
MCUCR is not used for the purpose you are using it. In fact, most of its bits are read-only.
Perhaps you meant to use EICRA and EICRB. These are the registers to set rising and falling edges.
I'm making some system that measure the environment light and turn off or on the light switch. To do this I have to use Atmega micro controller. The light measuring is done using LDR. LDR always output an Analog value and I have to convert it to digital value using AVR's ADC feature. I only have small knowledge in micro-controller programming. I write some code but I have no idea how to turn on relay switch using AVR.
this is my code
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
int main(void)
{
ADCSRA |= 1<<ADPS2;
ADMUX |= 1<<ADLAR;
ADCSRA |= 1<<ADIE;
ADCSRA |= 1<<ADEN;
sei();
ADCSRA |= 1<<ADSC;
while(1)
{
}
}
ISR(ADC_vect)
{
char adcres[4];
itoa (ADCH, adcres, 10);
PORTC=0x01; // Turn ON relay switch
ADCSRA |= 1<<ADSC;
}
I want to measure analog values using attached LDR and convert them in to digital values. Then after some per-define number relay should turn on and
I need somethins like this
lux = ldr_digital_value
if (lux > 5 )
{ PORTC=0x00; }
else
{ PORTC=0x01; }
How can I do that ?
assuming an ATmega8 (there are some differences between avrs)
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
volatile unsigned char lux=0; // the trick is the volatile.
int main(void)
{
ADCSRA = 1<<ADPS2; //slowest clock, ok
ADMUX = 1<<ADLAR; // you want 8 bit only? also channel 0 selected and external VREF.
// I suggest you to use Internal AVCC or internal 2.56V ref at first
ADCSRA |= 1<<ADIE; // with interrupts wow!
ADCSRA |= 1<<ADEN; // enable!
sei();
ADCSRA |= 1<<ADSC; // start first convertion
while(1)
{
if (lux > 5 ) //please choose an appropiate value.
{ PORTC=0x00; }
else
{ PORTC=0x01; }
}
}
ISR(ADC_vect)
{
lux =ADCH;
ADCSRA |= 1<<ADSC;
}
I'm trying to convert an old ASM programme to C.
I'm quite sure I extracted all the logic needed, but it simply doesn't work.
My goal is to use a timer compare match to toggle output pin, generating a buzz for speaker.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
// speaker pin
#define SPK _BV(PA0)
#define SYSPORT PORTA
#define SYSPIN PINA
#define SYSDDR DDRA
ISR(TIMER1_COMPA_vect)
{
SYSPIN |= SPK; // toggle speaker
}
void main()
{
cli(); // disable all interrupts
SYSDDR = 0b00000001; // SPK to output
SYSPORT = 0b00000001; // turn off SPK
TCCR0A = _BV(WGM01); // CTC mode
TCCR0B = _BV(CS01) | _BV(CS00); // Prescaler 64
OCR0A = 62; // compare match = 62 (output freq 1kHz)
TIMSK = _BV(OCIE0A); // enable timer interrupt
sei(); // enable all interrupts
while(1) {} // infinite loop.
}
This code does not make a single beep, not even clicking, nothing at all.
However, when I just use a loop and delay, it works beautifylly - proving the speaker is connected properly.
I cannot do this though, I have to do something else while it is making the sound.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
#include <stdint.h>
// speaker pin
#define SPK _BV(PA0)
// PORTA
#define SYSPORT PORTA
#define SYSPIN PINA
#define SYSDDR DDRA
void main()
{
SYSDDR = 0b00000001; // SPK to output
SYSPORT = 0b00000001; // turn off SPK
while(1) {
_delay_us(100);
SYSPIN |= SPK;
}
}
Fuses are set to
LFUSE = 0xE2
HFUSE = 0xDF
Frequency 4MHz.
Oh lol, I'm so inattentive
This should have been
ISR(TIMER0_COMPA_vect)
instead of
ISR(TIMER1_COMPA_vect)
It was handling a different timer :D
I'm having problems with serial communication. I've connected an AtMega644 to a serial LCD which takes 9600 8N1. I just get garbage. By garbage I'm just getting some U,P,T and # instead of the desired "U". I'm using the internal 8Mhz RC Osc with the fuses listed below. I suspect a timing issue but I'm not sure where I went wrong. I added a blinking LED and the timing looks right (eyeball and digital stopwatch). Any help is appreciated.
avrdude -pm644 -cavrisp2 -Pusb -b2400 -u
-Uflash:w:ImpactTarget.hex:a
-Ulfuse:w:0xe2:m
-Uhfuse:w:0xd8:m
-Uefuse:w:0xff:m
#define F_CPU 8000000
#define BAUDRATE 9600
#define UBRRVAL (F_CPU/(BAUDRATE*16UL)) -1
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
/***************************************************** USART_Init()
*
*
**/
void USART_Init () {
//Set baud rate
UBRR0H = (unsigned char)(UBRRVAL>>8); //high byte
UBRR0L = (unsigned char) UBRRVAL; //low byte
//Asynchronous normal speed
UCSR0A = (0<<U2X0);
//Enable Transmitter and Receiver and Interrupt on receive complete
UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0);
//page 186 Set asynchronous mode,no parity, 1 stop bit, 8 bit size
UCSR0C= (0<<UMSEL00)| (0<<UMSEL01)| //Async
(0<<UPM00) | (0<<UPM01) | //Parity None
(0<<USBS0) | //Stop bits 1
(0<<UCSZ02) | (1<<UCSZ01) |(1<<UCSZ00); //8 Bits
//enable interrupts
sei();
}
/******************************************** send_btye
* sends one byte to serial port
**/
void send_byte (char data) {
while ( ! (UCSR0A & (1<<UDRE0)) )
/* NOOP */;
UDR0 = data;
}
/**
* _delay_ms has a short time so this is an extension
*/
void delay_ms (int time) {
for (int i = 0; i < time; i++) {
_delay_ms(1);
}
}
/****************************** main *********/
int main () {
USART_Init();
DDRA = 0xff;
for (;;) {
send_byte('U');
delay_ms(500);
PORTA ^=_BV(PA0);
}
return 0;
}
Your UBRRVAL doesn't fully parenthesize its expression so when it is expanded in a context like UBRRVAL >> 8 the >> 8 does not apply the way you expect.
I think you're right - it's probably a timing issue: the internal RC oscillator is usually much too imprecise to use for USART.
I would try to attach an external crystal (and set the fuses correspondingly) and see if it helps.
this is exactly what took 3 days of my project time, just try to set Baudrate at (9600) and set the (X2) option for Baudrate. it should work.