uint32_t as Bit value to control outputs (relays) - c++

i have 25 relays which has min 15 different configurations, which have to be stored in a "array" or something simple...i have to switch those relays on/off (HiGH/LOW).
to use as less memory as possible i want to do it with a "trick" using bit's like:
char two = B1011011;
int mask = 1;
for(int i=0; i<7; i++){
if((mask & two) == 0) digitalWrite(pins[i], LOW); else
digitalWrite(pins[i], HIGH);
mask = mask << 1;
}
but char has only 8 bits and I NEED min 25 bits...
so now it the question NR.1, can i use uint32_t just in the same way as char just with 32 bits's? or something else?
uint32_t BIG1 = B10110110111011011111101101101
uint32_t BIG2 = B10110110111011011011101101101
uint32_t BIG3 = B10110110111111011011101101101
...
uint32_t BIG = B10110110111011011011101101101;//B... and 31 0s and 1s
uint32_t mask = 1;//????? not right???
for(int i=0; i<31; i++){
if((mask & two) == 0) digitalWrite(pins[i], LOW); else
digitalWrite(pins[i], HIGH);
mask = mask << 1;
};
what would be the mask then?
or is there a better/easyer/faster way to set OUTPUTS to the needed value?
Thank you in davance!

As I already told you in the other thread, the easiest and fastest way to do this is to deal with PORTS rather than individual pins.
For example, on the arduino UNO pins 0..7 map to port D pins 0..7, so when you do something like
uint8_t the_value_i_want = 0b01001000;
PORTD = the_value_i_want;
you write the pins 0..7 in a single instruction. Now, again with the uno, the complete mapping is
PORTD maps to Arduino digital pins 0 to 7
PORTB maps to Arduino digital pins 8 to 13. The two high bits (6 & 7) map to the crystal pins and are not usable
PORTC maps to Arduino analog pins 0 to 5. Bit 6 is the reset pin, so it's not usable, while bit 7 does not exist.
So things are a bit more complicated for the other ports. Well, the easiest way to handle this is making a function to mask the relevant bits. Just note that the masking is the same for port B and C, but this is just a coincidence.
#define PORT_B_C_MASK = 0x3F;
void write_with_mask(volatile uint8_t *p_register, uint8_t mask, uint8_t value)
{
*register = (*register | (value & mask)) & (value | ~mask);
}
Now you can write easily the instructions to write the value you want on the port. For instance, if you want to turn on pins 3, 6, 8 and 10, you just have to provide two values (one for port D, i.e pins 0..7, and one for port B, pins 8..13):
uint8_t the_value_i_want_8_13 = 0b000101;
uint8_t the_value_i_want_0_7 = 0b01001000;
write_with_mask(&PORTB,PORT_B_C_MASK,the_value_i_want_8_13);
PORTD = the_value_i_want_0_7;
Now, if you want to make a const matrix with all the possible values (again, this applies for the UNO only), you can just make a three-columns uint8_t matrix. Something like
int allvalues[][3] = { {0b001000, 0b001010, 0b00000001},
...};
In this case, with the first configuration (the reported one) pins A3, 0, 9, 11 will be turned on, the others will be off.
A possible function to apply this is
void apply_configuration(uint8_t index)
{
write_with_mask(&PORTC,PORT_B_C_MASK,allvalues[index][0]);
write_with_mask(&PORTB,PORT_B_C_MASK,allvalues[index][1]);
PORTD = allvalues[index][2];
}
This way you just have to provide the index for the configuration (the row) you want to apply.
If, for some reasons, you want to exclude some pins (e.g. pin 0 and 1, since they are the serial interface) you just have to include it in the mask. For instance:
#define PORT_B_MASK = 0x3F;
#define PORT_C_MASK = 0x0F;
#define PORT_D_MASK = 0xFC;
void apply_configuration(uint8_t index)
{
write_with_mask(&PORTC,PORT_C_MASK,allvalues[index][0]);
write_with_mask(&PORTB,PORT_B_MASK,allvalues[index][1]);
write_with_mask(&PORTD,PORT_D_MASK,allvalues[index][2]);
}
This way I excluded pins 0 and 1 (serial interface) and pins A4 and A5 (I2C interface).
Just one remark: I used the UNO as example, but you can use any board. Just look at the pin mapping to understand what is the association between ports and arduino pins.

Related

Problem using SevSegShift library with ESP32 and 74HC595

I'm trying to build a digital clock using ESP32. I cannot use the TM1637 display module because it's too small. So I'm using a generic common anode 4 digit 7 segment display. I used the Arduino SevSeg library on ESP32 and it worked perfectly. However, it consumed a lot of GPIO pins. Hence I used two 74HC595 registers and connected them exactly as described in the SevSegShift library (a fork of SevSeg library but with shift registers), like this:
Unfortunately, when I run the code, I get wrong output. No matter what number or characters I try to display, only the "b" segment of the corresponding digits are turned on, all other segments of all digits are turned off. For ex, if I try to display "1111", only the "b" segment of all digits are turned on, rest all segments of all digits are turned off. As a result only upper half of "1111" is displayed on the 7 segment display.
Here's the code:
#include <Arduino.h>
#include <SevSegShift.h>
#define SHIFT_PIN_DS 5 /* Data input PIN */
#define SHIFT_PIN_STCP 19 /* Shift Register Storage PIN */
#define SHIFT_PIN_SHCP 18 /* Shift Register Shift PIN */
SevSegShift sevseg(SHIFT_PIN_DS, SHIFT_PIN_SHCP, SHIFT_PIN_STCP); //Instantiate a seven segment controller object (with Shift Register functionality)
void setup() {
// put your setup code here, to run once:
byte numDigits = 4;
byte digitPins[] = {8+2, 8+5, 8+6, 2}; // of ShiftRegister(s) | 8+x (2nd Register)
byte segmentPins[] = {8+3, 8+7, 4, 6, 7, 8+4, 3, 5}; // of Shiftregister(s) | 8+x (2nd Register)
bool resistorsOnSegments = false;
byte hardwareConfig = COMMON_ANODE;
bool updateWithDelays = false;
bool leadingZeros = false;
bool disableDecPoint = false;
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
}
void loop() {
// put your main code here, to run repeatedly:
sevseg.setNumber(1111, 0);
sevseg.refreshDisplay();
delay(1);
}
What am I doing wrong? Is the circuit layout given above correct? More importantly, does the SevSegShift library work with ESP32?

ADXL345 library for sparkfun redboard turbo giving wrong readings

I am running an ADXL345 using I2C on both a sparkfun redboard turbo(processor samd21g18) and an old arduino uno (processor mega16u2). The library and sketch I'm using on both boards are same with the exception that the serial port is changed to SerialUSB to accommodate the redboard.
The issue appears to be that the uno interprets the xyz registers (2 bytes per axis in registers 0x32 - 0x37) as 16 bit twos compliment (as per the datasheet) and the redboard does not. The uno's output is correct and the redboard output which is incorrect is all positive integer output.
The image below shows the output for the redboard on left and uno on right for same relative position of the ADXL345).
I believe the offending code is in the following library code.
/*********************** READING ACCELERATION ***********************/
/* Reads Acceleration into Three Variables: x, y and z */
void ADXL345::readAccel(int *xyz){
readAccel(xyz, xyz + 1, xyz + 2);
}
void ADXL345::readAccel(int *x, int *y, int *z) {
readFrom(ADXL345_DATAX0, ADXL345_TO_READ, _buff); // Read Accel Data from ADXL345
// Each Axis # All g Ranges: 10 Bit Resolution (2 Bytes)
*x = (((int)_buff[1]) << 8) | _buff[0];
*y = (((int)_buff[3]) << 8) | _buff[2];
*z = (((int)_buff[5]) << 8) | _buff[4];
}
The sketch code is as follows:
#include <SparkFun_ADXL345.h>
/*********** COMMUNICATION SELECTION ***********/
/* Comment Out The One You Are Not Using */
//ADXL345 adxl = ADXL345(10); // USE FOR SPI COMMUNICATION, ADXL345(CS_PIN);
ADXL345 adxl = ADXL345(); // USE FOR I2C COMMUNICATION
/****************** INTERRUPT ******************/
/* Uncomment If Attaching Interrupt */
//int interruptPin = 2; // Setup pin 2 to be the interrupt pin (for most Arduino Boards)
/******************** SETUP ********************/
/* Configure ADXL345 Settings */
void setup(){
SerialUSB.begin(9600); // Start the SerialUSB terminal
SerialUSB.println("SparkFun ADXL345 Accelerometer Hook Up Guide Example");
SerialUSB.println();
adxl.powerOn(); // Power on the ADXL345
adxl.setRangeSetting(2); // Give the range settings
// Accepted values are 2g, 4g, 8g or 16g
// Higher Values = Wider Measurement Range
// Lower Values = Greater Sensitivity
}
/****************** MAIN CODE ******************/
/* Accelerometer Readings and Interrupt */
void loop(){
// Accelerometer Readings
int x,y,z;
adxl.readAccel(&x, &y, &z); // Read the accelerometer values and store them in variables declared above x,y,z
// Output Results to SerialUSB
/* UNCOMMENT TO VIEW X Y Z ACCELEROMETER VALUES */
SerialUSB.print(x);
SerialUSB.print(", ");
SerialUSB.print(y);
SerialUSB.print(", ");
SerialUSB.println(z);
adxl.printAllRegister();
delay(10);
}
The code that declares _buff[] which is in the header .h file:
private:
void writeTo(byte address, byte val);
void writeToI2C(byte address, byte val);
void writeToSPI(byte address, byte val);
void readFrom(byte address, int num, byte buff[]);
void readFromI2C(byte address, int num, byte buff[]);
void readFromSPI(byte address, int num, byte buff[]);
void setRegisterBit(byte regAdress, int bitPos, bool state);
bool getRegisterBit(byte regAdress, int bitPos);
byte _buff[6] ; // 6 Bytes Buffer
int _CS = 10;
bool I2C = true;
unsigned long SPIfreq = 5000000;
I believe that the library for the redboard needs to be modified to read the output registers 0x32-0x37 as 16 bit twos compliment. I am new to this programming environment so any help is appreciated.
Thanks - Jerry
I suspect from the Sparkfun board notes (https://www.sparkfun.com/products/14812) that the problem is that the Redboard Turbo is a 32-bit processor, with a 32-bit int instead of a 16-bit int. On such a processor, all 16 bit values stored in an int are positive.
To test my theory, run the following Sketch on your Redboard Turbo:
void setup() {
Serial.begin(9600);
while (!Serial) {}
Serial.print("sizeof(int) = ");
Serial.println(sizeof(int));
}
void loop() {
}
On my Arduino Uno - a 16-bit environment - the output says that an int is two bytes (16 bits)
sizeof(int) = 2
If your Redboard Turbo instead prints
sizeof(int) = 4
then its int is 4 bytes (32 bits).
I suspect that the library wasn't written for 32-bit processors, and may show several problems on the Redboard Turbo. To fix the particular readAccel() function, rewrite it to sign-extend the 16-bit number to 32 bits:
int16_t i16; // the 16-bit result, signed.
i16 = (int16_t) ((((uint16_t)_buff[1]) << 8) | _buff[0]);
*x = (int) i16;
i16 = (int16_t) ((((uint16_t)_buff[3]) << 8) | _buff[2]);
*y = (int) i16;
i16 = (int16_t) ((((uint16_t)_buff[5]) << 8) | _buff[4]);
*z = (int) i16;
By the way, in the above rewrite of readAccel() I've used uint16_t to make sure the byte shifting happens on an unsigned value, because left-shifting a signed number can produce unexpected results on some processors.

read address from atmel m90e26s using mcp2210

I'm working on a school project where we want to monitor the energy consumption using the atmel M90e26s chip.
I used the mcp2210 library and wrote this little testscript:
void talk(hid_device* handle) {
ChipSettingsDef chipDef;
//set GPIO pins to be CS
chipDef = GetChipSettings(handle);
for (int i = 0; i < 9; i++) {
chipDef.GP[i].PinDesignation = GP_PIN_DESIGNATION_CS;
chipDef.GP[i].GPIODirection = GPIO_DIRECTION_OUTPUT;
chipDef.GP[i].GPIOOutput = 1;
}
int r = SetChipSettings(handle, chipDef);
//configure SPI
SPITransferSettingsDef def;
def = GetSPITransferSettings(handle);
//chip select is GP4
def.ActiveChipSelectValue = 0xffef;
def.IdleChipSelectValue = 0xffff;
def.BitRate = 50000l;
def.SPIMode = 4;
//enable write
byte spiCmdBuffer[3];
//read 8 bytes
def.BytesPerSPITransfer = 3;
r = SetSPITransferSettings(handle, def);
if (r != 0) {
printf("Errror setting SPI parameters.\n");
return;
}
spiCmdBuffer[0] = 0x01; //0000 0011 read
spiCmdBuffer[1] = 0x00; //address 0x00
SPIDataTransferStatusDef def1 = SPISendReceive(handle, spiCmdBuffer, 3);
for (int i = 0; i < 8; i++)
printf("%hhu\n", def1.DataReceived[i]);
}
Any address I try, I get no respons. The problem seems this:
spiCmdBuffer[0] = 0x01; //0000 0011 read
spiCmdBuffer[1] = 0x00; //address 0x00
I know from the datasheet that the spi interface looks like this:
spi interface
Can somebody help me to find the address registers from the atm90e26? All the addresses look like '01H', but that is not hexadecimal and it's not 7 bit either.
Yes, as you suspected the problem is in how you set the contents of spiCmdBuffer. The ATM90E26 expects both the read/write flag and the register address to be in the first byte of the SPI transaction: the read/write flag must be put at the most significant bit (value 1 to read from a register, value 0 to write to a register), while the register address is in the 7 remaining bits. So for example to read the register at address 0x01 (SysStatus) the code would look like:
spiCmdBuffer[0] = 0x80 | 0x01; // read System Status
The 0x80 value sets the read/write flag in the most significant bit, and the other value indicates the register address. The second and third bytes of a 3-byte read sequence don't need to be set to anything, since they are ignored by the ATM90E26.
After calling SPISendReceive(), to extract the register contents (16 bits) you have to read the second and third byte (MSB first) from the data received in the read transaction, like below:
uint16_t regValue = (((uint16_t)def1.DataReceived[1]) << 8) | def1.DataReceived[2];

Best way to pass variables between Arduino and PC over serial?

I've written an Arduino sketch which reads data from a remote control receiver and returns a value between 0 and 1023 for that channel. I basically want to send this data (something in the format of channel:value, eg, Channel 1 : 1023, Channel 2 : 511) to a PC program (which I plan to write myself).
The most efficient way I can think to do this is to use two bytes of data, with the first 6 bits representing the channel (2^6 = 64 possible channels, way more than I need), and the last ten representing the value (2^10 = 1024, perfect). But I'm not sure on the best way to implement this in C++, or if this is even the most ideal way to do this. So:
What is the best way to craft individual bytes and work with binary numbers in C++? Preferably storing the values in memory as such (ie, no bool arrays, where each index takes up it's own byte). Two bytes of data is more than enough for what I need.
Is this the easiest/simplest/most efficient/recommended way to implement what I am trying to achieve? I basically want to pass variables as is between programs, are there any other ways to do this?
You can declare a packed struct to hold these two values:
struct chan_value_t
{
uint8_t channel : 6;
uint16_t value : 10;
};
But to send it as two bytes, you'll need to either (1) "union" it with a two-byte array:
union chan_value_t
{
struct {
uint8_t channel : 6;
uint16_t value : 10;
};
uint8_t bytes[2];
};
chan_value_t cv;
void setup()
{
Serial.begin( 9600 );
cv.channel = 2;
cv.value = 800;
for (int i=0; i<sizeof(cv.bytes); i++) {
Serial.print( cv.bytes[i], HEX );
Serial.print( ' ' );
}
Serial.println();
}
void loop() {}
(The struct is anonymous when nested in this union; the union has the name.)
Or (2) cast a pointer to the struct to a pointer to bytes:
struct chan_value_t {
uint8_t channel : 6;
uint16_t value : 10;
};
chan_value_t cv;
void setup()
{
Serial.begin( 9600 );
cv.channel = 2;
cv.value = 800;
uint8_t *bytes = (uint8_t *) &cv; // cast &cv to a pointer to bytes
for (int i=0; i<sizeof(cv); i++) {
Serial.print( bytes[i], HEX );
Serial.print( ' ' );
}
Serial.println();
}
void loop() {}
They both print the hexadecimal value of the bytes: 0x02 and 0xC8. 800 is 0x320, shifted left by 6 bits is 0xC800.
To send this to the PC, you may want to start with a special character sequence and finish with a checksum of some sort (Fletcher checksum is easy). Then it's easy to throw away garbage characters and know when there are transmission errors.
This is aimed at your no. 2 question.
OSC (OpenSoundControl) is a convenient way to send messages across different platforms and devices. Libraries exist for most platforms.
You could use the library OSC for Arduino and implement your own solution to the specification or using a library that fits your context.
The message you mention could be sent as /channel/1 /value/1023

What does this Arduino code do?

I understand what most of this is doing, however, the
if (digitalRead(miso)) { d |= 1; }
Is a line that I dont understand. THis code is from the Adafruit MAX31855 library which is used for Arduino boards. I am trying to port the code to my C8051F020 MCU in c. This code reads from a thermocouple and the MAX31855 is the digital interface to the MCU. here is the whole code for this file. I am not familiar with digitalRead(). The if statement posted above is where I am failing to interpret. This if statement is in the uint32_t Adafruit_MAX31855::spiread32(void) function.
/***************************************************
This is a library for the Adafruit Thermocouple Sensor w/MAX31855K
Designed specifically to work with the Adafruit Thermocouple Sensor
----> https://www.adafruit.com/products/269
These displays use SPI to communicate, 3 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include "Adafruit_MAX31855.h"
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>
Adafruit_MAX31855::Adafruit_MAX31855(int8_t SCLK, int8_t CS, int8_t MISO) {
sclk = SCLK;
cs = CS;
miso = MISO;
//define pin modes
pinMode(cs, OUTPUT);
pinMode(sclk, OUTPUT);
pinMode(miso, INPUT);
digitalWrite(cs, HIGH);
}
double Adafruit_MAX31855::readInternal(void) {
uint32_t v;
v = spiread32();
// ignore bottom 4 bits - they're just thermocouple data
v >>= 4;
// pull the bottom 11 bits off
float internal = v & 0x7FF;
internal *= 0.0625; // LSB = 0.0625 degrees
// check sign bit!
if (v & 0x800)
internal *= -1;
//Serial.print("\tInternal Temp: "); Serial.println(internal);
return internal;
}
double Adafruit_MAX31855::readCelsius(void) {
int32_t v;
v = spiread32();
//Serial.print("0x"); Serial.println(v, HEX);
/*
float internal = (v >> 4) & 0x7FF;
internal *= 0.0625;
if ((v >> 4) & 0x800)
internal *= -1;
Serial.print("\tInternal Temp: "); Serial.println(internal);
*/
if (v & 0x7) {
// uh oh, a serious problem!
return NAN;
}
// get rid of internal temp data, and any fault bits
v >>= 18;
//Serial.println(v, HEX);
// pull the bottom 13 bits off
int16_t temp = v & 0x3FFF;
// check sign bit
if (v & 0x2000)
temp |= 0xC000;
//Serial.println(temp);
double centigrade = v;
// LSB = 0.25 degrees C
centigrade *= 0.25;
return centigrade;
}
uint8_t Adafruit_MAX31855::readError() {
return spiread32() & 0x7;
}
double Adafruit_MAX31855::readFarenheit(void) {
float f = readCelsius();
f *= 9.0;
f /= 5.0;
f += 32;
return f;
}
uint32_t Adafruit_MAX31855::spiread32(void) {
int i;
uint32_t d = 0;
digitalWrite(sclk, LOW);
_delay_ms(1);
digitalWrite(cs, LOW);
_delay_ms(1);
for (i=31; i>=0; i--)
{
digitalWrite(sclk, LOW);
_delay_ms(1);
d <<= 1;
if (digitalRead(miso)) {
d |= 1;
}
digitalWrite(sclk, HIGH);
_delay_ms(1);
}
digitalWrite(cs, HIGH);
//Serial.println(d, HEX);
return d;
}
DigitalRead
Reads the value from a specified digital pin, either HIGH or LOW.
MISO (Master In Slave Out): the input of the Master's shift register, and the output of the Slave's shift register.
SPI overview
| is the bitwise OR operator.
d |= 1 is the shorthand notation for
d = d | 1
This code sets the last bit of d to 1 if the condition is true.
So what you're doing is reading the output of the slave register, and if it's 1, it's setting the last bit of d to 1. Right before that line, it shifts d left by one bit with d <<= 1;. And it does this in a loop:
Shift d left 1 bit
Read miso, if it's 1, set the least significant bit of d to 1.
repeat
MISO is the Master In/Slave Out pin on an SPI bus. In this code you're playing the part of the Master, so that is the pin where you read input from the slave. d |= 1 is just setting the last bit in d to 1 if it read a 1 from the slave (and all of the other bits will be unaffected). Every iteration it sets the last (LSB) bit of d to 1 if it read a 1, and then at the beginning of the next iteration it shifts d to the left.
I have no experience at all with this API, however...
If you view the digitalRead() documentation, all the function does is read the bit on the pin that you supply as an argument. It returns either HIGH or LOW.
So basically, what I see: if miso bit is on, make the first bit in d on.