I am writing a program for an nRF52 based board using the Redbear Arduino Library. Effectively treating my board as a BLE Nano 2.
I have a timer that ticks every x milliseconds, for example 50ms.
Inside that timer I would like to read data from an I2C sensor and add the reading to a buffer.
I am aware that by default, when inside the timer ISR, interrupts are disabled. I would like to know how to briefly re-enable the I2C interrupt, and get the sensor reading, then disable the interrupts again.
The interval between sensor readings is critical, and I don't just want to set a flag in the timer ISR as I don't know how long it will be before that flag is checked.
Please can someone instruct me on how to breifly enable the I2C interrupts from within a timer ISR?
I have experimented with:
__disable_irq();
__enable_irq();
NVIC_EnableIRQ(SPI0_TWI0_IRQn);
NRF_TWI0->INTENSET = 1;
No joy with any of these, I understand that some of them only work from certain locations in software, so were not functioning correctly within an ISR.
void setup() {
// put your setup code here, to run once
NVIC_SetPriority (TIMER0_IRQn, 2);
NVIC_SetPriority (TIMER1_IRQn, 2);
NVIC_SetPriority (TIMER2_IRQn, 2);
NVIC_SetPriority (SPI0_TWI0_IRQn, 3);
NVIC_SetPriority (SPI1_TWI1_IRQn, 3);
Serial.begin(9600);
Serial.println("Start ");
Serial.print("Priority of TWI0: ");
Serial.println(NVIC_GetPriority (SPI0_TWI0_IRQn));
Serial.println(NVIC_GetPriority (SPI1_TWI1_IRQn));
Serial.print("Priority of Ticker: ");
Serial.println(NVIC_GetPriority (TIMER0_IRQn));
Serial.println(NVIC_GetPriority (TIMER1_IRQn));
Serial.println(NVIC_GetPriority (TIMER2_IRQn));
Wire.begin();
__disable_irq();
__enable_irq();
ticker1s.attach(task_handle, 1);
Wire.requestFrom(0x02,6);
}
void task_handle(void) {
__enable_irq();
Serial.println("Task handle ");
Serial.println("-IRQ enable status: ");
Serial.println(NVIC_GetEnableIRQ(SPI0_TWI0_IRQn));
Serial.println(NVIC_GetEnableIRQ(SPI1_TWI1_IRQn));
NVIC_EnableIRQ(SPI0_TWI0_IRQn);
NRF_TWI0->INTENSET = 1;
NVIC_EnableIRQ(SPI1_TWI1_IRQn);
NRF_TWI1->INTENSET = 1;
Serial.println("-IRQ enable status: ");
Serial.println(NVIC_GetEnableIRQ (SPI0_TWI0_IRQn));
Serial.println(NVIC_GetEnableIRQ (SPI1_TWI1_IRQn));
delay(1000);
Wire.requestFrom(0x02,6);
}
By default, interrupts are not disabled in ISRs on Cortex-M, but rather interrupts are pre-empted based on the priorities set in NVIC (hence the "Nested" part of NVIC). This means that you should simply enable the I2C interrupt as you usually do, and then set the priority using NVIC_SetPriority(IRQn, priority).
Note however that priority numbers are ordered in reverse, so a priority of 0 would pre-empt a priority of say 6. In this case you would set the Timer interrupt priorities to say 1 and the I2C interrupt to 0.
Related
I want to make a device like Knocki(https://knocki.com), which essentially is a relay control using a vibration sensor. i can detect vibrations rn but the problem is, once i knock the relay blinks on and then turns off. i understand this is a lack of programming that is causing this. could someone help me write code which makes it so that when i knock the relay is turned on indefinitely; until I knock again to turn relay off.
And yes u can probably tell that this code is copied from somewhere(https://wiki.keyestudio.com/Ks0068_keyestudio_37_in_1_Sensor_Kit_for_Arduino_Starters#Project_21:_Vibration_Sensor).I took it from the home page of the vibration sensor. the code was initially so that every time i knocked, the onboard Arduino led lit up. Also, right now the relay is blinking faintly every time i knock(Although correctly,in sync with my knocks)
#define SensorLED 13
#define SensorINPUT 3 //Connect the sensor to digital Pin 3 which is Interrupts 1.
unsigned char state = 0;
int Relay = 5;
void setup()
{
pinMode(SensorLED, OUTPUT);
pinMode(SensorINPUT, INPUT);
attachInterrupt(1, blink, FALLING);// Trigger the blink function when the falling edge is detected
}
void loop()
{ if(state!=0)
{
state = 0;
digitalWrite(SensorLED,HIGH);
delay(500);
digitalWrite(Relay,HIGH);
}
else
digitalWrite(SensorLED,LOW);
digitalWrite(Relay,lOW);
}
void blink()//Interrupts function
{ state++;
Yes its in your code: The (bad) example works only because there is a
digitalWrite(SensorLED,HIGH);
->>> delay(500);
a delay for 1/2 sec to keep the led on.So as a check put an other delay after the relay line and it should go on for 1/2 sec too (so the led is lit 1 sec in total)
digitalWrite(SensorLED,HIGH);
delay(500);
digitalWrite(Relay,HIGH);
delay(500);
Thats just for checking -> NEXT STEP:
Get rid of the delays (see blinkwithoutdelay example in
Arduino->File->Examples->2.Digital -> blinkwithoutdelay
and introduce a second state variable e.g.
bool relayStateOn = false;
to get an independent on/off of the relay and the led.(If thats - what I understand -what you want to do)
If you feed your relay from the board, that is not the problem. Please, check the voltage in your relay when you try to set it on, if your voltage falls down, it means that this output to your relay does not supply the necessary current.
I made a nice code which generates fast PWM with 50% duty cycle and I can change the frequency with a potentiometer. It outputs straight and inverted channels with some dead time. I am using Arduino Micro aka ATmega32U4. The code is actually "Atmel" code. Code is working fine until I power Arduino Micro off and then on again.
I have programmed the code and registers so that the frequency is changeable from 10kHz to 100kHz. But after power on/off the frequency changes from 5kHz to 50kHz. After this has happened I have to program the board again using Arduino IDE, to make it work correctly. Again after power on/off it has changed. I am quite sure that one of the registers is overwritten by the "Arduino hardware abstraction layer" or however we should name it. I have not yet read out all the registers so I do not know which one is overwritten. I guess it's the prescaler.
How do I prevent this from happening? Should I write the register contents somewhere else? Or should I write it few times to be sure?
Why or how this is happening anyway?
Here's the code:
#define OSC1 5
#define OSC2 13
uint8_t read_reg1;
uint8_t read_reg2;
int pot, freq;
void setup() {
pinMode(OSC1, OUTPUT);
pinMode(OSC2, OUTPUT);
Serial.begin(9600);
cli(); // disable global interrupts
TCCR4A=0; // clear register
TCCR4B=0x06; // configure prescaler to 64 (CK = CLK / 64 = 1.5 MHz)
TCCR4C=0;
TCCR4D=0; // select Fast PWM operation (0 << WGM41)|(0 << WGM40)
PLLFRQ=(PLLFRQ&0xCF)|0x30; // select clock source and frequency
OCR4C=150; // select PWM frequency
OCR4A=150/2; // set duty cycle
DT4 = 0x55; // set dead times. DT = (1 / 48Mhz) * 0...15
// enable interrupt on timer4 overflow
TIMSK4|=(1 << TOIE4);
// This register write has to be after others. Otherwise the PWM generation will not work. I do not know why.
TCCR4A=0x42; // COM4A1..0 = 01, OC4A and !OC4A connected. PWM4A = 1 (activate channel A PWM output)
sei(); // enable global interrupts
}
void loop() {
//cli();
pot = analogRead(A0);
freq = map(pot, 0, 1023, 14, 166);
//sei();
/*
Serial.print("Pot value: ");
Serial.print(pot);
Serial.print("\tFreq value: ");
Serial.println(1500000/freq);
*/
}
ISR(TIMER4_OVF_vect){
OCR4C = freq;
OCR4A = freq / 2;
}
I am not sure exactly why you got different behavior right after programming, but the bootloader that the Arduino Micro uses (Caterina) does not perform a full reset after it runs, so changes that the bootloader made to the AVR's registers are often visible to the user's sketch.
I was able to fix the problem by removing the line that modifies PLLFRQ. Here is a simplified version of your code that always produces 3.31 kHz PWM:
void setup()
{
pinMode(5, OUTPUT);
pinMode(13, OUTPUT);
TCCR4A = 0;
TCCR4B = 0x06; // configure prescaler to 64 (CK = CLK / 64 = 1.5 MHz)
TCCR4C = 0;
TCCR4D = 0; // select Fast PWM operation (0 << WGM41)|(0 << WGM40)
OCR4C = 150; // select PWM frequency
OCR4A = 150 / 2; // set duty cycle
DT4 = 0x55; // set dead times. DT = (1 / 48Mhz) * 0...15
// This register write has to be after others.
// Otherwise the PWM generation will not work. I do not know why.
// COM4A1..0 = 01, OC4A and !OC4A connected.
// PWM4A = 1 (activate channel A PWM output)
TCCR4A = 0x42;
}
void loop()
{
}
It's not a great idea to mess with the PLL postscaler since it will probably affect every other Arduino library that uses timers, including the USB stack.
I am using an Arduino Uno, connected to a USB shield, a RFID shield(adafruit PN532), an LCD, EEPROM(24AA256) and a RTC module(DS1307). I will not post my code here because it is too large and it is separated in multiple files.
In my program, I realize that if my programs enters a certain functions, after entering function after function, if I use a delay() at the end of the function I am currently in, the arduino resets. An example of what I mean is below.
void a() { b(); }
void b() { c(); }
void c() { d(); }
void d()
{
lcd_string("Testing", 0x80);
delay(2000); <---- Arduino resets at the delay here
}
At first, I thought it was because my dynamic memory was at 80%, and when I compiled, they said the Arduino might have some stability issues. So I modified my code such that my dynamic memory is now 57%. Problem still exist.
I thought maybe the delay() function has some overflow or something, so I tried replacing the delay with the following code.
unsigned long timing;
timing = millis();
timing += 2000;
while(millis() < timing);
The Arduino still resets.
Next, I thought maybe because my arduino is connected to my PC, some serial pin might have been causing the reset, so I used an external Power to power up the arduino and disconnected the USB. The arduino still resets.
Next, I thought maybe Timer1 might have been crashing with the delay() function, although the delay function uses Timer0 so I disabled my Timer1 . The arduino still resets.
Is there any other possibilities that I am missing out? My program storage space is at 69% which I believe shouldn't be an issue.
Edit
Here is my code for Timer1 ISR
ISR(TIMER1_OVF_vect)
{
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 34286;// = (16*10^6) / (1*1024) - 1 (must be <65536)
TCCR1B |= (1 << CS12);
// enable timer compare interrupt
TIMSK1 |= (1 << TOIE1);
triggered = 1;
}
Any other interrupt of flags used are in the library header files.
I am using the following external libraries
USB Host shield library 2.0
Adafruit PN532 master
A little sample to come close to RAM corruption ...
#define MEM_PER_LEVEL 50
#define TRY_TO_SURVIVE 10
void KillMe(int level) {
byte dummy[MEM_PER_LEVEL];
for ( byte i = 0; i < MEM_PER_LEVEL; i++)
dummy[i]= i;
Serial.println(level);
delay(1000); // not sure why this would hurt more than others
if (level < TRY_TO_SURVIVE) KillMe(level+1);
for ( byte i = 0; i < MEM_PER_LEVEL; i++) {
if (dummy[i] != i) {
Serial.println(F("corruption happened"));
while(1) {} // HALT
}
}
if (level == 0)
Serial.println(F("survived"));
}
void setup() {
Serial.begin(9600);
KillMe(0);
}
void loop() { }
I had the same problem - wherever I put a delay in my setup function the Arduino would restart.
For me, the problem was an instance of SoftwareSerial with invalid pin numbers.
SoftwareSerial mySerial(30, 31);
Anyone else landing on this question should check their pin numbers are appropriate for the board they're targeting. Not sure why the crash only happens if a delay is called, would be interested if anyone has insight into this!
Using the Arduino Mini Pro 3.3V I just stumbled over a problem when switching between the "INTERNAL" and "DEFAULT" voltage reference for the ADC.
I want to measure the output of a voltage divider [GND - 110kOhm - A2 - 500kOhm - VCC] for calculating VCC. VCC has been measured as 3.3V. It is provided by a voltage regulator.
In the loop I firstly measure the voltage divider output with the internal reference and afterwards with the default voltage reference.
I saw code examples where people recommend to wait some milliseconds before reading the next value and the (analogReference() documentation) recommends to ignore the first readings after calling analogReference(). I follow these guidlines.
I'll provide a minimum example sketch:
// the setup function runs once when you press reset or power the board
void setup()
{
pinMode(A2, INPUT); // ADC pin
Serial.begin(9600);
Serial.println("----------------");
}
void burn8Readings(int pin)
{
for (int i = 0; i < 8; i++)
{
analogRead(pin);
}
}
// the loop function runs over and over again forever
void loop()
{
uint16_t nResult1, nResult2;
analogReference(INTERNAL); // set the ADC reference to 1.1V
delay(10); // idle some time
burn8Readings(A2); // make 8 readings but don't use them to ensure good reading after ADC reference change
nResult1 = analogRead(A2); // read actual value
analogReference(DEFAULT); // set the ADC reference back to internal for other measurements
delay(10); // idle again
burn8Readings(A2); // make 8 readings but don't use them to ensure good reading after ADC reference change
nResult2 = analogRead(A2); // do other measurements
// print result to serial interface..
Serial.print("1: ");
Serial.print(nResult1);
Serial.print(" - 2: ");
Serial.println(nResult2);
delay(2000);
}
The first pair of ADC results seems correct (553 / 184), but in the following iterations the first value is faulty without changing the actual voltage on the ADC pin. (240 / 183)
The ADC result of the DEFAULT reference is always fine.
For a 2.56V reference the value of 240 would be feasible. I know that some ATmegas use a 2.56V reference voltage, but the ATmega328 should have 1.1V only. Strangely the (ATmega328/P datasheet) mentions a 2.56V reference in an ADC example in chapter 28.7, so I'm confused.
Is there a possibility there is a 2.56V ADC reference in a certain ATmega328p version?
It turns out the similarity to the 2.56V was a coincidence and probably an error in the datasheet (or in my understanding).
The problem was that after the analogReference(INTERNAL) call the ADC value has to be read immediately! Not after some milliseconds as I did it. (Source)
Still it is important to also wait some milliseconds after doing the dummy readout. For me one readout and delay(5) was just enough, but I guess that depends on the charge left in the capacitor of the ADC: So I'd recommend higher delays.
The correct sequence is:
analogReference(INTERNAL); // set the ADC reference to 1.1V
burn8Readings(A2); // make 8 readings but don't use them
delay(10); // idle some time
nResult1 = analogRead(A2); // read actual value
and
analogReference(DEFAULT); // set the ADC reference back to internal
burn8Readings(A2); // make 8 readings but don't use them
delay(10); // idle again
nResult2 = analogRead(A2); // read actual value
Changing the reference back to DEFAULT seems to be less prone...but at least one readout still was necessary for precise results.
I hope no one has to spend time on this one anymore...
i'd like to put the SAM3X chip on sleepmode until a character arrives on the serial port. i was thinking of using an ausiliary flag in the Serial interrupt procedure in order to trigger the wake up procedure? what do you think abou? any advice or any other way i should follow or try?
I recommend reviewing .\arduino-1.5.2\hardware\arduino\sam\cores\arduino\UARTClass.cpp as its UARTClass::begin will detail how the Arduino Framework initializes the SAM's Serial IRQ with:
// Configure interrupts
_pUart->UART_IDR = 0xFFFFFFFF;
_pUart->UART_IER = UART_IER_RXRDY | UART_IER_OVRE | UART_IER_FRAME;
// Enable UART interrupt in NVIC
NVIC_EnableIRQ(_dwIrq);
// Enable receiver and transmitter
_pUart->UART_CR = UART_CR_RXEN | UART_CR_TXEN ;
Where you will need to ensure the baud rate generator is not stopped while in sleepmode.
Along with reading the SAM3X data sheet starting with 5.5.3. which leaves the peripheral clocks enabled.
looks like you may be able to insert the wake up into the UARTClass::IrqHandler
void UARTClass::IrqHandler( void )
{
uint32_t status = _pUart->UART_SR;
// Did we receive data ?
if ((status & UART_SR_RXRDY) == UART_SR_RXRDY)
{
// wake up!!!! Not sure if you even need to wakeup, it should from the sleepmode
_rx_buffer->store_char(_pUart->UART_RHR);
}
With regards to sleeping:
\arduino-1.5.2\hardware\arduino\sam\system\libsam\source\pmc.c
Line 972: void pmc_enable_sleepmode(uint8_t uc_type)
Line 988: void pmc_enable_waitmode(void)
Line 1009: void pmc_enable_backupmode(void)
So I would suspect the following:
pmc_enable_sleepmode(WFI);
would put the unit a sleep and the IrqHandler of the UART_SR_RXRDY would wake itself up, without any code change.
One other alternative would be to use the serial pin's IO as to trigger an interrupt.
attachInterrupt(0, EnableSerialRX, CHANGE);
It would though, loose at least the first byte. the trade off is that you can use the lower power modem of pmc_enable_backupmode() rather than sleep