Sensorless BLDC motor control BEMF section - arduino

I am trying to make a sensorless bldc motor control driver. I found an Arduino code and I want to convert it ARM Stm32. But I don't understand totally what happens in ISR interrupt part. Can anyone explain me shortly? Why used bldc_step&1 and when decreased i.
byte bldc_step = 0, motor_speed, pin_state;
void setup()
{
DDRD |= 0xE0; // configure pins 5, 6 and 7 as outputs
PORTD = 0x00;
DDRB |= 0x0E; // configure pins 9, 10 and 11 as outputs
PORTB = 0x31;
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
}
// pin change interrupt 2 (PCINT2) ISR
ISR (PCINT2_vect)
{
if( (PIND & PCMSK2) != pin_state )
return;
// BEMF debounce
for(byte i = 0; i < 20; i++)
{
if(bldc_step & 1){
if(PIND & PCMSK2) i -= 1;
}
else {
if(!(PIND & PCMSK2)) i -= 1;
}
}
bldc_move();
bldc_step++;
bldc_step %= 6;
}
// BLDC motor commutation function
void bldc_move()
{
switch(bldc_step)
{
case 0:
AH_BL();
BEMF_C_FALLING();
break;
case 1:
AH_CL();
BEMF_B_RISING();
break;
case 2:
BH_CL();
BEMF_A_FALLING();
break;
case 3:
BH_AL();
BEMF_C_RISING();
break;
case 4:
CH_AL();
BEMF_B_FALLING();
break;
case 5:
CH_BL();
BEMF_A_RISING();
}
}
PCICR = 4; // enable pin change interrupt for pins PCINT23..16 (Arduino 0 to 7)
void BEMF_A_RISING()
{
PCMSK2 = 0x04; // enable Arduino pin 2 (PCINT18) interrupt, others are disabled
pin_state = 0x04;
}
void BEMF_A_FALLING()
{
PCMSK2 = 0x04; // enable Arduino pin 2 (PCINT18) interrupt, others are disabled
pin_state = 0;
}
void BEMF_B_RISING()
{
PCMSK2 = 0x08; // enable Arduino pin 3 (PCINT19) interrupt, others are disabled
pin_state = 0x08;
}
void BEMF_B_FALLING()
{
PCMSK2 = 0x08; // enable Arduino pin 3 (PCINT19) interrupt, others are disabled
pin_state = 0;
}
void BEMF_C_RISING()
{
PCMSK2 = 0x10; // enable Arduino pin 4 (PCINT20) interrupt, others are disabled
pin_state = 0x10;
}
void BEMF_C_FALLING()
{
PCMSK2 = 0x10; // enable Arduino pin 4 (PCINT20) interrupt, others are disabled
pin_state = 0;
}
void AH_BL()
{
PORTD &= ~0xA0;
PORTD |= 0x40;
TCCR1A = 0; // turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)
TCCR2A = 0x81; //
}
void AH_CL()
{
PORTD &= ~0xC0;
PORTD |= 0x20;
TCCR1A = 0; // turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)
TCCR2A = 0x81; //
}
void BH_CL()
{
PORTD &= ~0xC0;
PORTD |= 0x20;
TCCR2A = 0; // turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)
TCCR1A = 0x21; //
}
void BH_AL()
{
PORTD &= ~0x60;
PORTD |= 0x80;
TCCR2A = 0; // turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)
TCCR1A = 0x21; //
}
void CH_AL()
{
PORTD &= ~0x60;
PORTD |= 0x80;
TCCR2A = 0; // turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)
TCCR1A = 0x81; //
}
void CH_BL()
{
PORTD &= ~0xA0;
PORTD |= 0x40;
TCCR2A = 0; // turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)
TCCR1A = 0x81; //
}
void SET_PWM_DUTY(byte duty)
{
OCR1A = duty; // set pin 9 PWM duty cycle
OCR1B = duty; // set pin 10 PWM duty cycle
OCR2A = duty; // set pin 11 PWM duty cycle
}

The speed of the motor depends of the applied voltage D.V where the D is the duty.
Fm = D.VP.KV.poles/120 = D.Vp/M
Tm = M/(D.Vp)
The ideal delay after bemf detection is 30 deg :
delay = Tm/12 = M/(12.D.Vp) = M'/(D.Vp) : M'=M/12
M' – Volt*seconds for the 30° delay after zero crossing detection.
The comparator ISR is counting up only during on time. As wider is the PWM pulse the faster is the counting.
Loop maximum time:
m = N.ticks.Cycles
Cycles are the ticks per instruction.
Loop is counting (up) to N only during ton on rising or on falling BEMF. The counted PWM pulses will be:
Pc = m/ton or Pc = m/(D.Ts)
The estimated time of the loop is:
dt = Pc.Ts = m.Ts/(D.Ts) = m/D
The required delay and the time of the loop must be equal: delay = dt
M'/(Vp.D) = m/D or m = M'/Vp
This method is also called "Back-EMF Integration Method".
M' = D.Vp.Tm'
is the integrated bemf voltage from ZCP to the next commutation. You can read more
about this briefly here:
https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3231115/
Adjusting N and PWM frequency (Fs=1/Ts) can give you almost perfect 30 deg delay after bemf detection. The poles and Kv of the motor should be known. This code will not work with any motor without adjustment.
Cheers

This code is for debouncing the back electromagnetic force (BEMF) signal, equivalent code is in https://github.com/esden/open-bldc-mk/blob/master/bldc.c
ISR(ANA_COMP_vect){
unsigned char i;
/* debounce the bemf signal */
for(i=0; i<BEMF_DEBOUNCE_COUNT; i++){
if(bldc_phase & 1){
if(BEMF_L) i -= BEMF_DEBOUNCE_DEC;
}else{
if(BEMF_H) i -= BEMF_DEBOUNCE_DEC;
}
}
For debouncing and why it is done see https://en.wikipedia.org/wiki/Switch#Contact_bounce
The exact algorithm implemented here is debouncing switches with vertical counters, cf https://www.compuphase.com/electronics/debouncing.htm - Debouncing switches with vertical counters
In bldc_step & 1 the & operator is bitwise AND i.e. 45 & 35 = 33 or 00101101 & 00100011 = 00100001 in binary
bldc_step is a byte and bitwise-ANDing it with 1 is equivalent to testing if the last bit of bldc_step is set. With PIND & PCMSK2 is checked if PIND is high or low
This code and the rest of the debouncing loop implements the vertical counter described in https://www.compuphase.com/electronics/debouncing.htm

Related

How to downsize code that uses DHT11 for AVR (Attiny45)

I'm trying to build a small hygrometer based on the DHT11 and I'm having a bit of an "issue" with the code size. I want to run it on an Attiny45 and it's a wee bit too big (352 bytes too big to be exact). I am aware that I could just use an Attiny85 and have space to spare or don't use a bootloader and barely fit it in (94%) but I kind of want to make my life harder than it needs to be and figure out how to reduce size since it'll probably come in handy in the future. Treat it as a learning experience if you will.
What it's supposed to do:
Read DHT11 input
Display results on 2 two-digit 7-segment displays (so I guess 4 7-segments in total)
Go to sleep most of the time to preserve battery
Wake up every 8 seconds to update sensor values (without turning on the display)
Wake up on a button press to display the values for humidity and temperature
Side note: 7-segments are adressed via two 74HC595s of which I am using 7 outputs each for the displays and 1 each for a transistor that connects the display in question to GND. There's a schematic at the bottom if you're interested.
As pointed out, my main issue is code size so if anyone has any tips on how to reduce that (or any other tips how to improve the code) please let me know.
I hope I'm asking the question properly, if not please let me know.
Compiler output:
Sketch uses 3872 bytes (110%) of program storage space. Maximum is 3520 bytes.text section exceeds available space in board
Global variables use 107 bytes (41%) of dynamic memory, leaving 149 bytes for local variables. Maximum is 256 bytes.
Sketch too big; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing it.
Error compiling for board ATtiny45/85 (Optiboot).
Code:
/*
Humidity/Temperature sensor setup with DHT-11
Two digits for humidity
Two digits for temperature
Button to wake up from sleep
NPNs activated via 8th bit in 74HC595s, always alternating
Author: ElectroBadger
Date: 2021-11-02
Version: 1.0
*/
/*
Reduce power consumption:
- Run at 1 MHz internal clock
- Turn off ADC
- Use SLEEP_MODE_PWR_DOWN
*/
#include "DHT.h" //DHT-11 sensor
#include <avr/sleep.h> // Sleep Modes
#include <avr/power.h> // Power management
#include <avr/wdt.h> //Doggy stuff
//define attiny pins
#define INT_PIN PB4
#define DATA PB1
#define SENSOR PB3
#define LATCH PB2
#define CLK PB0
//define other stuff
#define SENSOR_TYPE DHT11
#define LED_DELAY 50
//changing variables
short ones_data; //16-bits for display of ones
short tens_data; //16-bits for display of tens
byte sevSeg, measurements; //7-segment bit pattern / wait time between LEDs [ms] / # of measurements taken
bool firstPair, btnPress; //tracks which pair of 7-segments is on; tracks button presses
uint32_t oldMillis, sleepTimer; //tracks the last acquisition time and wakeup time
//Initialize sensor
DHT dht(SENSOR, SENSOR_TYPE);
//Shifts 16 bits out MSB first, on the rising edge of the clock.
void shiftOut(int dataPin, int clockPin, short toBeSent){
int i=0;
int pinState = 0;
//Clear everything out just in case
digitalWrite(dataPin, 0);
digitalWrite(clockPin, 0);
//Loop through bits in the data bytes, COUNTING DOWN in the for loop so that
//0b00000000 00000001 or "1" will go through such that it will be pin Q0 that lights.
for(i=0; i<=15; i++){
digitalWrite(clockPin, 0);
//if the value passed to myDataOut AND a bitmask result
//is true then set pinState to 1
if(toBeSent & (1<<i)){
pinState = 1;
}
else{
pinState = 0;
}
digitalWrite(dataPin, pinState); //Sets the pin to HIGH or LOW depending on pinState
digitalWrite(clockPin, 1); //Shifts bits on upstroke of clock pin
digitalWrite(dataPin, 0); //Zero the data pin after shift to prevent bleed through
}
digitalWrite(clockPin, 0); //Stop shifting
}
//Converts an int <10 to a bit pattern for 7-segment displays
short toSegments(int value){
byte pattern = 0b00000000; //create empty pattern
//Using a switch...case (3878 bytes) if...else if...else uses 3946 bytes
switch(value){
case 0:
pattern = 0b01111110;
break;
case 1:
pattern = 0b00110000;
break;
case 2:
pattern = 0b01101101;
break;
case 3:
pattern = 0b01111001;
break;
case 4:
pattern = 0b00110011;
break;
case 5:
pattern = 0b01011011;
break;
case 6:
pattern = 0b01011111;
break;
case 7:
pattern = 0b01110000;
break;
case 8:
pattern = 0b01111111;
break;
case 9:
pattern = 0b01111011;
break;
default:
pattern = 0b00000000;
break;
}
return pattern;
}
void goToSleep(){
//Turn off 7-segments and NPNs
digitalWrite(LATCH, 0);
shiftOut(DATA, CLK, 0b0000000000000000);
digitalWrite(LATCH, 1);
//Set deep sleep mode
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
ADCSRA = 0; // turn off ADC
power_all_disable (); // power off ADC, Timer 0 and 1, serial interface
cli(); // timed sequence coming up, so disable interrupts
btnPress = false;
measurements = 0;
resetWatchdog (); // get watchdog ready
sleep_enable (); // ready to sleep
sei(); // interrupts are required now
sleep_cpu (); // sleep
sleep_disable (); // precaution
power_all_enable (); // power everything back on
}
ISR(PCINT_VECTOR){
btnPress = true;
sleepTimer = millis();
}
// watchdog interrupt
ISR(WDT_vect){
wdt_disable(); //disable watchdog
}
void resetWatchdog(){
MCUSR = 0; //clear various "reset" flags
WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF); //allow changes, disable reset, clear existing interrupt
//set interrupt mode and an interval (WDE must be changed from 1 to 0 here)
WDTCR = bit (WDIE) | bit (WDP3) | bit (WDP0); //set WDIE, and 8 seconds delay
wdt_reset(); //pat the dog
}
void setup(){
resetWatchdog(); // do this first in case WDT fires
cli(); //Disable interrupts during setup
pinMode(INT_PIN, INPUT_PULLUP); //Set interrupt pin as input w/ internal pullup
pinMode(DATA, OUTPUT); //Set serial data as output
pinMode(CLK, OUTPUT); //Set shift register clock as output
pinMode(LATCH, OUTPUT); //Set output register (latch) clock as output
// Interrupts
PCMSK = bit(INT_PIN); //Enable interrupt handler (ISR)
GIFR |= bit(PCIF); // clear any outstanding interrupts
GIMSK |= bit(PCIE); //Enable PCINT interrupt in the general interrupt mask
//default conditions
/* bit 0-6: ones digits
bit 7: NPN for units digits
bit 8-14: ones digits
bit 15: NPN for tens digits
*/
ones_data = 0b0000000000000000;
tens_data = 0b0000000000000000;
measurements = 0;
firstPair = true;
btnPress = false;
oldMillis = 0;
sleepTimer = 0;
//Start sensor
dht.begin();
delay(1000); //wait 1s for sensor to stabilize
sei(); //Enable interrupts after setup
}
void loop(){
if((millis()-oldMillis) > 1000){
//Slow sensor, so readings may be up to 2 seconds old
byte hum = dht.readHumidity(); //Read humidity
byte temp = dht.readTemperature(); //Read temperatuer in °C
//update tens bit string
tens_data = 0b0000000000000000; //reset to all 0s
sevSeg = toSegments(hum/10); //convert tens of humidity to 7-segment logic
tens_data |= sevSeg; // bitwise OR the result with the output short
tens_data = tens_data << 8; //shift by 8 so it's almost in the right place (see below)
sevSeg = toSegments(temp/10); //convert tens of temperature to 7-segment logic
tens_data |= sevSeg; // bitwise OR the result with the output short
tens_data = tens_data << 1; //shift by 1 so everything is in the right place
tens_data |= 0b0000000100000000; //set NPN for tens pair to active and ones NPN to inactive
//update ones bit string
ones_data = 0b0000000000000000; //reset to all 0s
sevSeg = toSegments(hum%10); //convert ones of humidity to 7-segment logic
ones_data |= sevSeg; // bitwise OR the result with the output short
ones_data = ones_data << 8; //shift by 8 so it's almost in the right place (see below)
sevSeg = toSegments(temp%10); //convert ones of temperature to 7-segment logic
ones_data |= sevSeg; // bitwise OR the result with the output short
ones_data = ones_data << 1; //shift by 1 so everything is in the right place
ones_data |= 0b0000000000000001; //set NPN for ones pair to active and tens NPN to inactive
oldMillis = millis(); //I don't much care about the few ms lost
} //during data acquisition
if(btnPress){
//shift out the next batch of data to the display
digitalWrite(LATCH, 0); //Set latch pin LOW so nothing gets shifted out
if(firstPair){
shiftOut(DATA, CLK, tens_data); //Shift out LED states for 7-segments of tens
firstPair = false;
}
else{
shiftOut(DATA, CLK, ones_data); //Shift out LED states for 7-segments of ones
firstPair = true;
}
digitalWrite(LATCH, 1); //sent everything out in parallel
delay(LED_DELAY); //wait for some time until switching to the other displays
if((millis()-sleepTimer) > 6000){ //Sleep after 6s display time
goToSleep();
}
}
else{
if(measurements > 5){
goToSleep();
}
}
}
DHT11 hygrometer schematic
Okay so thanks to the input of Mat I tried substituting the DHT11 library with something more sleek, which took me a while to get up and running. I ended up using this as a base, edited around a bit and commented heavily for my benefit.
I added my updated code below for anyone interested (thanks for pointing out the correct highlighting issue), there's also a github with the rest of the design files.
Seems the library is really heavy, as the compiler output shows:
Compiler output:
Sketch uses 2354 bytes (66%) of program storage space. Maximum is 3520 bytes.
Global variables use 104 bytes (40%) of dynamic memory, leaving 152 bytes for local variables. Maximum is 256 bytes.
Code:
/*
Humidity/Temperature sensor setup with DHT-11
Two digits for humidity
Two digits for temperature
Button to wake up from sleep
NPNs activated via 8th bit in 74HC595s, always alternating
Author: ElectroBadger
Date: 2021-11-09
Version: 2.0
*/
/*
Reduce power consumption:
- Run at 1 MHz internal clock
- Turn off ADC
- Use SLEEP_MODE_PWR_DOWN
*/
//#include "DHT.h" // DHT-11 sensor
#include <avr/sleep.h> // Sleep Modes
#include <avr/power.h> // Power management
#include <avr/wdt.h> // Doggy stuff
// define attiny pins
#define INT_PIN PB4
#define DATA PB1
#define SENSOR PB3
#define LATCH PB2
#define CLK PB0
// define other stuff
//#define SENSOR_TYPE DHT11
#define LED_DELAY 50
//fixed variables
//array lookup for number display; ascending order: 0, 1, 2, ...
const byte numLookup[] = {
0b01111110, //0
0b00110000, //1
0b01101101, //2
0b01111001, //3
0b00110011, //4
0b01011011, //5
0b01011111, //6
0b01110000, //7
0b01111111, //8
0b01111011 //9
};
// changing variables
short ones_data; // 16-bits for display of ones
short tens_data; // 16-bits for display of tens
byte sevSeg, measurements; // 7-segment bit pattern / wait time between LEDs [ms] / # of measurements taken
bool firstPair, btnPress; // tracks which pair of 7-segments is on; tracks button presses
uint32_t oldMillis, sleepTimer; // tracks the last acquisition time and wakeup time
byte humI, humD, tempI, tempD; // values of humidity and temperature (we're only gonna need integral parts but I need all for the checksum)
// Initialize sensor
//DHT dht(SENSOR, SENSOR_TYPE);
// Shifts 16 bits out MSB first, on the rising edge of the clock.
void shiftOut(int dataPin, int clockPin, short toBeSent) {
int i = 0;
int pinState = 0;
// Clear everything out just in case
digitalWrite(dataPin, 0);
digitalWrite(clockPin, 0);
// Loop through bits in the data bytes
for (i = 0; i <= 15; i++) {
digitalWrite(clockPin, 0);
// if the value AND a bitmask result is true then set pinState to 1
if (toBeSent & (1 << i)) {
pinState = 1;
}
else {
pinState = 0;
}
digitalWrite(dataPin, pinState); // sets the pin to HIGH or LOW depending on pinState
digitalWrite(clockPin, 1); // shifts bits on upstroke of clock pin
digitalWrite(dataPin, 0); // zero the data pin after shift to prevent bleed through
}
digitalWrite(clockPin, 0); // Stop shifting
}
void start_signal(byte SENSOR_PIN) {
pinMode(SENSOR_PIN, OUTPUT); // set pin as output
digitalWrite(SENSOR_PIN, LOW); // set pin LOW
delay(18); // wait 18 ms
digitalWrite(SENSOR_PIN, HIGH); // set pin HIGH
pinMode(SENSOR_PIN, INPUT_PULLUP); // set pin as input and pull to VCC (10k)
}
boolean read_dht11(byte SENSOR_PIN) {
uint16_t rawHumidity = 0;
uint16_t rawTemperature = 0;
uint8_t checkSum = 0;
uint16_t data = 0;
unsigned long startTime;
for (int8_t i = -3; i < 80; i++) { // loop 80 iterations, representing 40 bits * 2 (HIGH + LOW)
byte high_time; // stores the HIGH time of the signal
startTime = micros(); // stores the time the data transfer started
// sensor should pull line LOW and keep for 80µs (while SENSOR_PIN == HIGH)
// then pull HIGH and keep for 80µs (while SENSOR_PIN == LOW)
// then pull LOW again, aka send data (while SENSOR_PIN == HIGH)
do { // waits for sensor to respond
high_time = (unsigned long)(micros() - startTime); // update HIGH time
if (high_time > 90) { // times out after 90 microseconds
Serial.println("ERROR_TIMEOUT");
return;
}
}
while (digitalRead(SENSOR_PIN) == (i & 1) ? HIGH : LOW);
// actual data starts at iteration 0
if (i >= 0 && (i & 1)) { // if counter is odd, do this (only counts t_on time and ignores t_off)
data <<= 1; // left shift data stream by 1 since we are at a the next bit
// TON of bit 0 is maximum 30µs and of bit 1 is at least 68µs
if (high_time > 30) {
data |= 1; // we got a one
}
}
switch ( i ) {
case 31: // bit 0-16 is humidity
rawHumidity = data;
break;
case 63: // bit 17-32 is temperature
rawTemperature = data;
case 79: // bit 33-40 is checksum
checkSum = data;
data = 0;
break;
}
}
// Humidity
humI = rawHumidity >> 8;
rawHumidity = rawHumidity << 8;
humD = rawHumidity >> 8;
// Temperature
tempI = rawTemperature >> 8;
rawTemperature = rawTemperature << 8;
tempD = rawTemperature >> 8;
if ((byte)checkSum == (byte)(tempI + tempD + humI + humD)) {
return true;
}
else {
return false;
}
}
void goToSleep() {
// Turn off 7-segments and NPNs
digitalWrite(LATCH, 0);
shiftOut(DATA, CLK, 0b0000000000000000);
digitalWrite(LATCH, 1);
set_sleep_mode (SLEEP_MODE_PWR_DOWN); // Set deep sleep mode
ADCSRA = 0; // turn off ADC
power_all_disable (); // power off ADC, Timer 0 and 1, serial interface
cli(); // timed sequence coming up, so disable interrupts
btnPress = false; // reset button flag
measurements = 0; // reset measurement counter
resetWatchdog (); // get watchdog ready
sleep_enable (); // ready to sleep
sei(); // interrupts are required now
sleep_cpu (); // sleep
sleep_disable (); // precaution
power_all_enable (); // power everything back on
}
ISR(PCINT0_vect) {
btnPress = true;
sleepTimer = millis();
}
// watchdog interrupt
ISR(WDT_vect) {
wdt_disable(); // disable watchdog
}
void resetWatchdog() {
MCUSR = 0; //clear various "reset" flags
WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF); //allow changes, disable reset, clear existing interrupt
WDTCR = bit (WDIE) | bit (WDP3) | bit (WDP0); //set WDIE, and 8 seconds delay
wdt_reset();
}
void setup() {
resetWatchdog(); // do this first in case WDT fires
cli(); // disable interrupts during setup
pinMode(INT_PIN, INPUT_PULLUP); // set interrupt pin as input w/ internal pullup
pinMode(DATA, OUTPUT); //set serial data as output
pinMode(CLK, OUTPUT); //set shift register clock as output
pinMode(LATCH, OUTPUT); //set output register (latch) clock as output
pinMode(SENSOR, INPUT); //set DHT11 pin as input
// Interrupts
PCMSK = bit(INT_PIN); // enable interrupt handler (ISR)
GIFR |= bit(PCIF); // clear any outstanding interrupts
GIMSK |= bit(PCIE); // enable PCINT interrupt in the general interrupt mask
//default conditions
/* bit 0-6: ones digits
bit 7: NPN for units digits
bit 8-14: ones digits
bit 15: NPN for tens digits
*/
ones_data = 0b0000000000000000;
tens_data = 0b0000000000000000;
measurements = 0;
firstPair = true;
btnPress = false;
oldMillis = 0;
sleepTimer = 0;
humI = 0;
humD = 0;
tempI = 0;
tempD = 0;
// Start sensor
//dht.begin();
sei(); // enable interrupts after setup
}
void loop() {
if ((millis() - oldMillis) > 1000) {
// slow sensor, so readings may be up to 2 seconds old
//byte hum = dht.readHumidity(); //Read humidity
//byte temp = dht.readTemperature(); //Read temperatuer in °C
delay(2000); // wait for DHT11 to start up
start_signal(SENSOR); // send start sequence
if(read_dht11(SENSOR)){
// update tens bit string
tens_data = 0b0000000000000000; // reset to all 0s
tens_data |= numLookup[humI / 10]; // bitwise OR the result with the output short
tens_data = tens_data << 8; // shift by 8 so it's almost in the right place (see below)
tens_data |= numLookup[tempI / 10]; // bitwise OR the result with the output short
tens_data = tens_data << 1; // shift by 1 so everything is in the right place
tens_data |= 0b0000000100000000; // set NPN for tens pair to active and ones NPN to inactive
// update ones bit string
ones_data = 0b0000000000000000; // reset to all 0s
ones_data |= numLookup[humI % 10]; // bitwise OR the result with the output short
ones_data = ones_data << 8; // shift by 8 so it's almost in the right place (see below)
ones_data |= numLookup[tempI % 10]; // bitwise OR the result with the output short
ones_data = ones_data << 1; // shift by 1 so everything is in the right place
ones_data |= 0b0000000000000001; // set NPN for ones pair to active and tens NPN to inactive
}
else{
tens_data = 0b1001111110011100;
ones_data = 0b0000101000001011;
}
oldMillis = millis(); // I don't much care about the few ms lost during data acquisition
}
if (btnPress) {
// shift out the next batch of data to the display
digitalWrite(LATCH, 0); // Set latch pin LOW so nothing gets shifted out
if (firstPair) {
shiftOut(DATA, CLK, tens_data); // shift out LED states for 7-segments of tens
firstPair = false; // reset first digit flag
}
else {
shiftOut(DATA, CLK, ones_data); //Shift out LED states for 7-segments of ones
firstPair = true; //set first digit flag
}
digitalWrite(LATCH, 1); //sent everything out in parallel
delay(LED_DELAY); //wait for some time until switching to the other displays
if ((millis() - sleepTimer) > 6000) { //Sleep after 6s display time
goToSleep();
}
}
else {
if (measurements > 3) {
goToSleep();
}
}
}

Received data are correct on scope not in program

I use Arduino shiftIn instruction to receive data from a MLX90316.
It uses one wire for MOSI an MISO.
Data consists of 4 bytes (2+2)
First 2 bytes are an angle value. Second 2 bytes ares the reversed value of angle (1st complement)
First 2 bytes are correct on scope and program (0x00 and 0x22)
Second 2 bytes are correct on scope (0xFF and 0XDD), but are frequently (not always) wrong in program (0xFF and 0xDC or 0xFF and 0xCC...)
In fact only last byte is sometimes wrong when I print it.
I have tried to change Serial speed to 9600 bps (same result)
I have tried to change delayMicroseconds value before last shiftIn (same result)
/* MLX90316 Rotary Position Sensor */
int readAngle();
int pinSS = 10; // Green Wire
int pinDATA = 11; // Yellow Wire
int pinSCK = 13; // Grey Wire
unsigned int r1=0;
unsigned int r2=0;
unsigned int r3=0;
unsigned int r4=0;
int angle;
void setup(){
delayMicroseconds(16000); // 16ms slave start-up
pinMode(pinSS,OUTPUT); // Pin Slave Select
pinMode(pinSCK, OUTPUT); // Pin Clock
digitalWrite(pinSS, HIGH); // de-select chip
Serial.begin(115200);
Serial.println("MLX90316 Rotary Position Sensor");
}
void loop() {
angle = readAngle();
Serial.println("");
Serial.print("r1=");Serial.println(r1,HEX);
Serial.print("r2=");Serial.println(r2,HEX);
Serial.print("r3=");Serial.println(r3,HEX);
Serial.print("r4=");Serial.println(r4,HEX);
delay(10000);
}
int readAngle() {
// Start with clock LOW
digitalWrite(pinSCK, LOW);
// Data pin in write mode
pinMode(pinDATA, OUTPUT);
// take the SS pin low to select the chip:
digitalWrite(pinSS, LOW);
delayMicroseconds(30);
// Send START bytes
shiftOut(pinDATA, pinSCK, MSBFIRST, 0xAA);
delayMicroseconds(25);
shiftOut(pinDATA, pinSCK, MSBFIRST, 0xFF);
delayMicroseconds(30); // 30 us between START bytes and DATA
// Data pin in read mode
pinMode(pinDATA, INPUT_PULLUP);
// Receive data
r1 = shiftIn(pinDATA, pinSCK, MSBFIRST);
delayMicroseconds(25);
r2 = shiftIn(pinDATA, pinSCK, MSBFIRST);
delayMicroseconds(25);
r3 = shiftIn(pinDATA, pinSCK, MSBFIRST);
delayMicroseconds(30);
r4 = shiftIn(pinDATA, pinSCK, MSBFIRST);
// take the SS pin high to de-select the chip:
delayMicroseconds(5);
digitalWrite(pinSS, HIGH);
}
I expect the output of 0x00 0x22 0xFF 0xDD
I don't know which Arduino you are using but the shift in and out functions are very simple software implementations.
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
uint8_t value = 0;
uint8_t i;
for (i = 0; i < 8; ++i) {
digitalWrite(clockPin, HIGH);
if (bitOrder == LSBFIRST)
value |= digitalRead(dataPin) << i;
else
value |= digitalRead(dataPin) << (7 - i);
digitalWrite(clockPin, LOW);
}
return value;
}
The MLX90316 allows only a clock speed of 145kHz, so this direct port access could be too fast and the data is not ready when you read it.
But since you have a scope available you could simply check that timing and write your own shiftIn if necessary.
I think this "version" should work for your chip (untested)
uint8_t shiftIn2(uint8_t dataPin, uint8_t clockPin) {
uint8_t value = 0;
uint8_t i;
for (i = 0; i < 8; ++i) {
digitalWrite(clockPin, HIGH);
delayMicroseconds(4);
digitalWrite(clockPin, LOW);
value |= digitalRead(dataPin) << (7 - i);
delayMicroseconds(4);
}
return value;
}

microphone sph0645 with I2S less sensitive after watchdog sleep with Adafruit Feather M0

I am using the Adafruit Feather M0 RFM69 with the Adafruit I2S MEMS Microphone Breakout SPH0645. Every second I take a reading (sampleRate = 16000, bits per sample = 32) using the I2S library and send it over the radio. This all works fine.
My problem is that, when I want to save power, I am getting weird readings after I wake the board from sleep (using Adafruit_SleepyDog library). The microphone somewhat still works, although it is much less sensitive, only picks up loud sounds and also returns 60dB in a quiet room. When I don't put it to sleep, in the same sound setting, I get 40dB. However, if I put a delay of 250ms after waking up, the microphone works fine again, like before, but this is obviously not saving energy then.
I wonder why this is happening. Is there something I can do to get the microphone to work quicker? I checked the datasheet, but it only says: "When Vdd is applied the microphone senses the
CLOCK line, if the frequency is greater than 900KHz, the microphone enters the normal mode of operation." This should not even take a few ms though?
Thanks in advance
#include <I2S.h>
#include <Adafruit_SleepyDog.h>
#include <SPI.h>
#include <RH_RF69.h>
/************ Radio Setup ***************/
#define RF69_FREQ 433.0
#define SLEEP
//#if defined(ARDUINO_SAMD_FEATHER_M0) // Feather M0 w/Radio
#define RFM69_CS 8
#define RFM69_INT 3
#define RFM69_RST 4
#define LED 13
//#endif
// radio
// Singleton instance of the radio driver
RH_RF69 rf69(RFM69_CS, RFM69_INT);
int transmit_interval = 1000;
int time_counter = 0;
int packetnum = 0;
// MIC
#define SAMPLES 1024//2048 // make it a power of two for best DMA performance
int samples[SAMPLES];
int measurementsdB = 0;
int current_measure;
#define ADC_SOUND_REF 65
#define DB_SOUND_REF 41
int sampleRate1 = 16000;
int bitsPerSample1 = 32;
typedef struct
{
uint8_t measurementdB = 123;
uint8_t battery = 111;
uint8_t test = 222;
} RadioMessage;
RadioMessage struct_message;
void setup()
{
delay(2000); // Wait so its easier to program
Serial.begin(115200);
//while (!Serial) { delay(1); } // wait until serial console is open, remove if not tethered to computer
// Init Mic
if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate1, bitsPerSample1)) {
while (1); // do nothing
}
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
pinMode(RFM69_RST, OUTPUT);
digitalWrite(RFM69_RST, LOW);
Serial.println("Feather RFM69 TX Test!");
Serial.println();
// manual reset
digitalWrite(RFM69_RST, HIGH);
delay(10);
digitalWrite(RFM69_RST, LOW);
delay(10);
if (!rf69.init()) {
Serial.println("RFM69 radio init failed");
while (1);
}
Serial.println("RFM69 radio init OK!");
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module)
// No encryption
if (!rf69.setFrequency(RF69_FREQ)) {
Serial.println("setFrequency failed");
}
// If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the
// ishighpowermodule flag set like this:
rf69.setTxPower(20, true); // range from 14-20 for power, 2nd arg must be true for 69HCW
// The encryption key has to be the same as the one in the server
uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
rf69.setEncryptionKey(key);
Serial.print("RFM69 radio #"); Serial.print((int)RF69_FREQ); Serial.println(" MHz");
//GCLK->GENCTRL.bit.RUNSTDBY=1; // !! can go
}
void loop() {
Serial.println("START");
///// MIC
//PM->APBCMASK.reg |= PM_APBCMASK_I2S;
int a = 0;
while (a == 0) a = I2S.available();
uint8_t current_measure = sample_audio_signal(samples);
///// RADIO
if (true)//((time_counter + transmit_interval) < millis())
{
struct_message.measurementdB = current_measure;
//struct_message.battery = measuredvbat;
// Send a message!
/*
Serial.print("Array content: ");
uint8_t* bla = (uint8_t*) &struct_message;
for (int i = 0; i < 3; i++)
{
Serial.println(bla[i]);
}*/
rf69.send((const uint8_t*) &struct_message, sizeof(struct_message));
rf69.waitPacketSent();
Serial.print("Wait for reply");
// Now wait for a reply
uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
if (rf69.waitAvailableTimeout(100)) {
// Should be a reply message for us now
if (rf69.recv(buf, &len)) {
Serial.print("Got a reply: ");
Serial.println((char*)buf);
} else {
Serial.println("Receive failed");
}
} else {
Serial.println("No reply, is another RFM69 listening?");
}
Serial.println("Radio sleeping");
rf69.sleep();
time_counter = millis();
}
// sleep time
#ifdef SLEEP
int sleepMS = Watchdog.sleep(10);
delay(250);
#else
delay(1000);
#endif
Serial.println("loop ended");
}
void Blink(byte PIN, byte DELAY_MS, byte loops) {
for (byte i=0; i<loops; i++) {
digitalWrite(PIN,HIGH);
delay(DELAY_MS);
digitalWrite(PIN,LOW);
delay(DELAY_MS);
}
}
float sample_audio_signal(int samples[])
{
for (int i=0; i<SAMPLES; i++) {
int sample = 0;
while ((sample == 0) || (sample == -1) ) {
sample = I2S.read();
}
// convert to 18 bit signed
sample >>= 14;
samples[i] = sample;
}
// ok we have the samples, get the mean (avg)
float meanval = 0;
for (int i=0; i<SAMPLES; i++) {
meanval += samples[i];
}
meanval /= SAMPLES;
// subtract it from all samples to get a 'normalized' output
for (int i=0; i<SAMPLES; i++) {
samples[i] -= meanval;
}
// find the 'peak to peak' max
float maxsample, minsample;
minsample = 100000;
maxsample = -100000;
for (int i=0; i<SAMPLES; i++) {
minsample = min(minsample, samples[i]);
maxsample = max(maxsample, samples[i]);
}
int newdB = 20 * log10((float)maxsample / (float)ADC_SOUND_REF) + DB_SOUND_REF;
return newdB;
Ok, the best I got it down to is 3.8mA. I only got so far by leaving the voltage regulator and the internal oscillator (DFLL) on during sleeping.
After adding the following code to my setup routine, when board goes to sleep, the microphone still works after waking up:
SYSCTRL->DFLLCTRL.bit.RUNSTDBY=1;
SYSCTRL->VREG.bit.RUNSTDBY=1;
However, ideally I would like to get much less than that, but then the mic doesn't work...

Using arduino to control leds as a pattern

I have this code here which sends some keys to windows and also lights up an led attached under each button. I think its right so the led will toggle with the button. What I wanted to achieve ontop of this was so that if no button states hadn't changed for 30 seconds then it goes into a mode where all three leds flash rapidly 3 times then the leds randomly flashes (Like a pinball machine when its not in use). After any input then it goes back to the normal mode
/* Arduino USB Keyboard HID demo
* Cut/Copy/Paste Keys
*/
#define KEY_LEFT_CTRL 0x01
#define KEY_LEFT_SHIFT 0x02
#define KEY_RIGHT_CTRL 0x10
#define KEY_RIGHT_SHIFT 0x20
uint8_t buf[8] = {
0 }; /* Keyboard report buffer */
#define PIN_COPY 5
#define PIN_CUT 6
#define PIN_PASTE 7
#define LED_COPY 8
#define LED_CUT 9
#define LED_PASTE 10
int state = 1;
void setup()
{
Serial.begin(9600);
pinMode(PIN_COPY, INPUT);
pinMode(PIN_CUT, INPUT);
pinMode(PIN_PASTE, INPUT);
// Enable internal pull-ups
digitalWrite(PIN_COPY, 1);
digitalWrite(PIN_CUT, 1);
digitalWrite(PIN_PASTE, 1);
delay(200);
}
void loop()
{
state = digitalRead(PIN_CUT);
if (state != 1) {
buf[0] = KEY_LEFT_CTRL; // Ctrl
buf[2] = 27; // Letter X
// buf[2] = 123; // Cut key: Less portable
Serial.write(buf, 8); // Ssend keypress
digitalWrite(LED_CUT, HIGH);
releaseKey();
}
state = digitalRead(PIN_COPY);
if (state != 1) {
buf[0] = KEY_LEFT_CTRL; // Ctrl
buf[2] = 6; // Letter C
// buf[2] = 124; // Copy key: Less portable
Serial.write(buf, 8); // Send keypress
digitalWrite(LED_COPY, HIGH);
releaseKey();
}
state = digitalRead(PIN_PASTE);
if (state != 1) {
buf[0] = KEY_LEFT_CTRL; // Ctrl
buf[2] = 25; // Letter V
// buf[2] = 125; // Paste key: Less portable
Serial.write(buf, 8); // Send keypress
digitalWrite(LED_PASTE, HIGH);
releaseKey();
}
}
void releaseKey()
{
buf[0] = 0;
buf[2] = 0;
Serial.write(buf, 8); // Release key
delay(500);
digitalWrite(LED_COPY, LOW);
digitalWrite(LED_CUT, LOW);
digitalWrite(LED_PASTE, LOW);
}
For refrence this is the article I was using http://mitchtech.net/arduino-usb-hid-keyboard/
i recomend you to look for Finite State Machine in Google.
Here is one link to implement it in C++
http://www.drdobbs.com/cpp/state-machine-design-in-c/184401236
here is other link to implement it in Arduino
http://playground.arduino.cc/Code/SMlib
it's exactly what you need to do what you want.
i hope this help you!

How to control an Mcp41010 wiper position with an analog value

I am using an Mcp41010 digipot chip and am wondering how to vary the wiper position of the chip with an analog input voltage that I can adjust, I need a way of decrementing(--) the wiper position if the voltage goes over a certain point and incrementing(++) the wiper position of the chip back to the normal position this is some code that i found that just fades the wiper position up and down I need a way of controlling it. I am still very new to arduino so sorry if my explanation was clear enough.
int CS_signal = 12; // Chip Select signal onsul pin 2 of Arduino
int CLK_signal = 52; // Clock signal on pin 4 of Arduino
int MOSI_signal = 51; // MOSI signal on pin 5 of Arduino
byte cmd_byte2 = B00010001 ; // Command byte
int initial_value = 100; // Setting up the initial value
void initialize() { // send the command byte of value 100 (initial value)
spi_out(CS_signal, cmd_byte2, initial_value);
}
void spi_out(int CS, byte cmd_byte, byte data_byte){ // we need this function to send command byte and data byte to the chip
digitalWrite (CS, LOW); // to start the transmission, the chip select must be low
spi_transfer(cmd_byte); // invio il COMMAND BYTE
delay(2);
spi_transfer(data_byte); // invio il DATA BYTE
delay(2);
digitalWrite(CS, HIGH); // to stop the transmission, the chip select must be high
}
void spi_transfer(byte working) {
for(int i = 1; i <= 8; i++) { // Set up a loop of 8 iterations (8 bits in a byte)
if (working > 127) {
digitalWrite (MOSI_signal,HIGH) ; // If the MSB is a 1 then set MOSI high
} else {
digitalWrite (MOSI_signal, LOW) ; } // If the MSB is a 0 then set MOSI low
digitalWrite (CLK_signal,HIGH) ; // Pulse the CLK_signal high
working = working << 1 ; // Bit-shift the working byte
digitalWrite(CLK_signal,LOW) ; // Pulse the CLK_signal low
}
}
void setup() {
pinMode (CS_signal, OUTPUT);
pinMode (CLK_signal, OUTPUT);
pinMode (MOSI_signal, OUTPUT);
initialize();
Serial.begin(9600); // setting the serial speed
Serial.println("ready!");
}
void loop() {
for (int i = 0; i < 255; i++) {
spi_out(CS_signal, cmd_byte2, i);
Serial.println(i); delay(10);
}
for (int i = 255; i > 0; --i) {
spi_out(CS_signal, cmd_byte2, i);
Serial.println(i);
delay(10);
}
}
int CS_signal = 12; // Chip Select signal onsul pin 2 of Arduino
int CLK_signal = 52; // Clock signal on pin 4 of Arduino
int MOSI_signal = 51; // MOSI signal on pin 5 of Arduino
byte cmd_byte2 = B00010001 ; // Command byte
int initial_value = 100; // Setting up the initial value
void initialize() { // send the command byte of value 100 (initial value)
spi_out(CS_signal, cmd_byte2, initial_value);
}
void spi_out(int CS, byte cmd_byte, byte data_byte){ // we need this function to send command byte and data byte to the chip
digitalWrite (CS, LOW); // to start the transmission, the chip select must be low
spi_transfer(cmd_byte); // invio il COMMAND BYTE
delay(2);
spi_transfer(data_byte); // invio il DATA BYTE
delay(2);
digitalWrite(CS, HIGH); // to stop the transmission, the chip select must be high
}
void spi_transfer(byte working) {
for(int i = 1; i <= 8; i++) { // Set up a loop of 8 iterations (8 bits in a byte)
if (working > 127) {
digitalWrite (MOSI_signal,HIGH) ; // If the MSB is a 1 then set MOSI high
} else {
digitalWrite (MOSI_signal, LOW) ; } // If the MSB is a 0 then set MOSI low
digitalWrite (CLK_signal,HIGH) ; // Pulse the CLK_signal high
working = working << 1 ; // Bit-shift the working byte
digitalWrite(CLK_signal,LOW) ; // Pulse the CLK_signal low
}
}
void setup() {
pinMode (CS_signal, OUTPUT);
pinMode (CLK_signal, OUTPUT);
pinMode (MOSI_signal, OUTPUT);
initialize();
Serial.begin(9600); // setting the serial speed
Serial.println("ready!");
}
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
if(sensorValue <= 200){
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage);
int i = sensorValue;{
spi_out(CS_signal, cmd_byte2, i);
Serial.println(i);
}
}
}

Resources