Reading 32-bit SPI data using 16-bit processor (dsPIC) - spi

I am interfacing a M90E32AS energy meter IC with dsPIC33F series processor. I successfully read voltage and current values. I try to read power values also, as per the datasheet the power registers are 32-bits wide. I tried the following code to read the 32 bit value but I am unsuccessful. Help me to rectify the error.
int PmB_read()
{
CS=0;
SPI2BUF=SBUF=0x80B2;
while(SPI2STATbits.SPITBF==1){}
SPI2BUF=0x0;
while(SPI2STATbits.SPITBF==1){}
delay();
CS=1;
HiByte = SPI2BUF;
return HiByte;
}
int PmBLSB_read()
{
CS=0;
SPI2BUF=SBUF=0x80C2;
while(SPI2STATbits.SPITBF==1){}
SPI2BUF=0x0;
while(SPI2STATbits.SPITBF==1){}
delay();
CS=1;
LoByte = SPI2BUF;
TPmB = (HiByte << 16)| LoByte;
Total = TPmB * 0.00032;
return Total;
}
Here is the data sheet screen shot for power register

Related

Distortion in ESP32 I2S audio playback with external DAC for sample frequency higher than 20kSps

Hardware: ESP32 DevKitV1, PCM5102 breakout board, SD-card adapter.
Software: Arduino framework.
For some time I am struggling with audio playback using a I2S DAC external to ESP32.
The problem is I can only play without distortion for low sample frequencies, i.e. below 20kSps.
I have been studying the documentation, https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/i2s.html, and numerous other sources but sill haven't managed to fix this.
I2S configuration function:
esp_err_t I2Smixer::i2sConfig(int bclkPin, int lrckPin, int dinPin, int sample_rate)
{
// i2s configuration: Tx to ext DAC, 2's complement 16-bit PCM, mono,
const i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_CHANNEL_MONO), // only tx, external DAC
.sample_rate = sample_rate,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, // single channel
// .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL3, // highest interrupt priority that can be handeled in c
.dma_buf_count = 128, //16,
.dma_buf_len = 128, // 64
.use_apll = false,
.tx_desc_auto_clear = true};
const i2s_pin_config_t pin_config = {
.bck_io_num = bclkPin, //this is BCK pin
.ws_io_num = lrckPin, // this is LRCK pin
.data_out_num = dinPin, // this is DATA output pin
.data_in_num = I2S_PIN_NO_CHANGE // Not used
};
esp_err_t ret1 = i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL);
esp_err_t ret2 = i2s_set_pin((i2s_port_t)i2s_num, &pin_config);
esp_err_t ret3 = i2s_set_sample_rates((i2s_port_t)i2s_num, sample_rate);
// i2s_adc_disable((i2s_port_t)i2s_num);
// esp_err_t ret3 = rtc_clk_apll_enable(1, 15, 8, 5, 6);
return ret1 + ret2 + ret3;
}
A wave file, which was created in a 16 bit mono PCM, 44.1kHz format, is opened:
File sample_file = SD.open("/test.wav")
In the main loop, the samples are fed to the I2S driver.
esp_err_t I2Smixer::loop()
{
esp_err_t ret1 = ESP_OK, ret2 = ESP_OK;
int32_t output = 0;
if (sample_file.available())
{
if (sample_file.size() - sample_file.position() > 2) // bytes left
{
int16_t tmp; // 16 bits signed PCM assumed
sample_file.read((uint8_t *)&tmp, 2);
output =(int32_t)tmp;
}
else
{
sample_file.close();
}
}
size_t i2s_bytes_write;
int16_t int16_t_output = (int16_t)output;
ret1 = i2s_write((i2s_port_t)i2s_num, &int16_t_output, 2, &i2s_bytes_write, portMAX_DELAY);
if (i2s_bytes_write != 2)
ret2 = ESP_FAIL;
return ret1 + ret2;
}
This works fine for sample rates up to 20 kSps.
For a sample rate of 32k or 44.1k heavy distortion occurs. I suspect that this is caused by the I2S DMA Tx buffer.
If the number of DMA buffers (dma_buf_count) and the buffer length (dma_buf_len) is increased, then the sound is played fine at first. Subsequently, after a short time, the distortion kicks in again. I cannot measure this short time span, maybe around a second, but I did notice it depends on the dma_buf_count and dma_buf_len.
Next to this, I tried increasing the CPU frequency to 240MHz, no improvement.
Further I tried to play a file from SPIFSS, no improvement.
I am out of ideas right now, has anyone encountered this issue also?
Reading one sample at a time and pushing it to the I2S driver will not be the most efficient usage of the driver. You are using just 2 bytes in every 128 byte DMA buffer. That leaves just a single sample period to push the next sample before the DMA buffer is "starved".
Read the file in 128 byte (64 sample) chunks and write the whole chunk to the I2S in order to use the DMA effectively.
Depending on the file-system implementation it may be a little more efficient too to use larger chunks that are sympathetic to the file-system's media, sector size and DMA buffering.

Arduino - Reading Serial Data

I am trying to send information to an Arduino Mega 2560 using serial data in order to control both LED Pixel Strips and conventional christmas light strings. I am also using VIXEN lighting software.
I can control one strip of LED pixels from Vixen using this code in the Arduino loop() function;
Serial.readBytes((char*)leds, NUM_LEDS * 3);//buffer to store things in, length (number of bytes to read)
FastLED.show();//refresh the pixel LED's
I can also control a relay (or multiple relays) for the conventional lights using this code;
#define CHANNEL_01 7 //Pin #7 on the Arduino Mega board
void setup()
{
// Begin serial communication
Serial.begin(BAUD_RATE);
#define CHANNEL_COUNT 1
int channels[] = {CHANNEL_01}
int incomingByte[16];
// Define baud rate. This figure must match that of your profile configuration in Vixen!
#define BAUD_RATE 9600
// Set up each channel as an output
for(int i = 0; i < CHANNEL_COUNT; i++)
{
pinMode(channels[i], OUTPUT);
}
}
void loop()
{
if (Serial.available() >= CHANNEL_COUNT)
{
// Read data from Vixen, store in array
for (int i = 0; i < CHANNEL_COUNT; i++)
{
incomingByte[i] = Serial.read();
}
// Write data from array to a pin on Arduino
for (int i = 0; i < CHANNEL_COUNT; i++)
{
digitalWrite(channels[i], incomingByte[i]);
}
}
}
The problem is that I cannot do both of these things. I can either assign the 150 bytes of LED data to the LED strip and it works fine, OR, I can run the relays and they work fine. I have not been able to figure out how to chop up the bytes from the serial data and send it to the appropriate pin. For example, maybe I want to control a relay using pin 7 and a strip of LED pixels using pin 6.
The strip of pixel LED's consumes the first 150 bytes of data from the serial data. But how can I get the next one byte that controls a relay that turns on and off the conventional christmas light string? The byte that controls the light string would be the 151'st in the serial data. Is there a way to specify the 151'st byte? Serial.read() does nothing more than read the first byte (I think). How can a user iterate through the bytes of serial data and select only the ones they want?
When you do the Serial.readBytes((char*)leds, NUM_LEDS * 3); you read the first 150 bytes, assuming you have 50 LEDs. So the next byte pending in the serial buffer would be the 151'st byte, therefore if you call Serial.read() after Serial.readBytes((char*)leds, NUM_LEDS * 3); you would get that byte.
Note that you can use one byte to controle 8 relays if you want, one bit per relay, by using bitRead()
An example.
bool relayState[8];
Serial.readBytes((char*)leds, NUM_LEDS * 3);
byte relays = Serial.read();
for(byte i=0;i<8;i++){
relayState[i] = bitRead(relays, i);
}
for(byte i=0;i<8;i++) {
digitalWrite(relay[i], relayState[i]);
}
Then a value of 1 would turn on relay 0, a value of 2 would turn on relay 1, a value of 3 would turn on relay 0 and relay 1, etc.
To solve this problem I bought an Arduino Uno to run the standard (non-LED) lights separate from the LED lights which run off an Arduino MEGA 2560. The non-LED lights are run on one controller in the Vixen Lights software. The controller has 4 outputs (channels), one for each of the non-LED light sets. Each channel will control one line on a solid state relay. The Arduino Uno runs the relays using this code;
#define PIN1 7 //Pin number seven
#define PIN2 6 //Pin number six
#define PIN3 5 //Pin number five
#define PIN4 4 //Pin number four
#define BAUD_RATE 9600 //just running 4 relay switches so we don't need much speed
#define CHANNEL_COUNT 4 //there are 4 channels coming from the Vixen controller
int bt[4]; //a variable we will use in the loop section of code
int x; //another variable we will use in the loop section of code
void setup() {
delay(1000); //a little delay to give Uno some time on startup
Serial.begin(BAUD_RATE); //set the baud rate of the serial stream
pinMode(PIN1, OUTPUT); //set the four pins on the Arduino Uno to output
pinMode(PIN2, OUTPUT);
pinMode(PIN3, OUTPUT);
pinMode(PIN4, OUTPUT);
}
void loop() {
if (Serial.available() >= CHANNEL_COUNT) {
for (X = 0; x < CHANNEL_COUNT; x++) { //for every channel...
bt[x] = Serial.read(); //we read a byte from the serial stream buffer and store it in an array for later use
}
digitalWrite(PIN1, bt[0]); //we tell the pins on the arduino what to do...
digitalWrite(PIN2, bt[1]); //using the array of integers that holds the byte values from the serial stream...
digitalWrite(PIN3, bt[2]);
digitalWrite(PIN4, bt[3]);
}
}
The LED's run off a second controller in the Vixen Lights software. I have two 12 volt, 50 pixel LED strips of type WS2811. The Arduino uses the FastLED library that can be downloaded for free from FastLED.io. What I found was that there is one byte of garbage data that comes in the serial stream for the LED strips and I had to move past that byte of data in order for the LED's to receive the correct bytes of data to control their color, position etc. I use this code to run my LED's off the Arduino MEGA 2560;
#include <FastLED.h> //include the FastLED library in the Arduino project
#define LED_PIN1 7 //I am going to run one strip of 50 LEDs off of pin 7 on the MEGA
#define LED_PIN2 6 //I am going to run a second strip of 50 LEDs off of pin 6 on the MEGA
#define BAUD_RATE 115200
#define NUM_LEDS 50
//It took me some time to figure out that my two pixel strips are set
//to different color codes. Why? I don't know, but they are.
#define RGB_ORDER RGB //one of my pixel strips is set to use RGB color codes
#define BRG_ORDER BRG //the second strip came from the factory with BRG color codes
#define LED_TYPE WS2811 //what type of LEDs are they? Mine are WS2811, yours may be different.
//create an array to hold the FastLED CRBG code for each of the 50 pixels in the
//first strip.
CRGB leds1[NUM_LEDS];
//create another array to hold the FastLED CRBG codes for each of the 50 pixels in
//the second strip.
CRGB leds2[NUM_LEDS];
int g; //a variable we will use in the loop section
int bufferGarbage[1]; //THIS IS THE KEY TO MAKING THIS WHOLE THING WORK. WE NEED TO
//GET PAST THE FIRST MYSTERY BYTE THAT IS SENT TO THE ARDUINO MEGA FROM THE VIXEN
//LIGHTS SOFTWARE. So we create a variable we will use in the loop section of code.
void setup() {
delay(1000);
Serial.begin(BAUD_RATE);
pinMode(LED_PIN1, OUTPUT); //set our pins to output. PIN1 is pin 6 on the Arduino board.
pinMode(LED_PIN2, OUTPUT); //set our pins to output. PIN2 is pin 7 on the Arduino board.
//This line sets up the first pixel strip to run using FastLED
FastLED<LED_TYPE, LED_PIN1, RGB_ORDER>(leds1, NUM_LEDS).setCorrection(TypicalLEDStrip);
//This line sets up the second pixel strip to run using FastLED
FastLED<LED_TYPE, LED_PIN2, BRG_ORDER>(leds2, NUM_LEDS).setCorrection(TypicalLEDStrip);
}
void loop() {
if (Serial.available() >= 0) { //if there is data in the serial stream
//bufferGarbage is to capture the first byte of garbage that comes across.
//Without this the LED's are out of sync.
//In my case if I didn't capture this byte the first pixel on my
//second LED strip would match the color code that should be on the last
//pixel of the first strip. We don't do anything with this byte.
//but we need to read it from the serial stream so we can move to the
//next byte in the stream.
bufferGarbage[0] = Serial.read();
//then we need to populate the leds1 array so FastLED can tell the pixels what to do.
//We have 50 pixels in the strip and each pixel has a CRGB property that uses
//a red, green, and blue attribute. So for each LED we need to capture 3
//bytes from the serial stream. 50 LEDs * 3 bytes each means we need to read
//150 bytes of data from the serial stream.
for (g = 0; g < NUM_LEDS; g++) {
Serial.readBytes( ( char*)(&leds1[g], 3);
}
for (g = 0; g < NUM_LEDS; g++) {//then we read the next 150 bytes for the second strip of LEDs
Serial.readBytes( ( char*)(&leds2[g], 3);
}
FastLED.show(); //then we tell FastLED to show the pixels!
}
}

RFduino not pulling NMEA strings from GPS

I'm having trouble using the TinyGPS library to parse Lat and Lon. Is this library compatible with the RFduino? I can read NMEA strings by loading a blank sketch to the RFduino and then just opening the Serial Monitor, so I know that the GPS data is going through the serial port, but when I try to get the Lat or Lon into a variable, it fills the variable with 999999999. I'm sending this data through BLE to an android. If I don't try to get GPS data, I can send any value I want in the lat or lon variables and it appears in my custom Android App. I read somewhere that the softserial library doesn't work on rfduino. Is this true? If not, I would be able to print my data through the hard serial port, making troubleshooting much easier. Below I've attached the code I'm using on my RFduino. Any advice would be appreciated.
// CODE //
#include <RFduinoBLE.h>
#include <TinyGPS.h>
TinyGPS gps;
long lat = 5; //Load lat/lon with junk value for testing
long lon = 6;
char latBuf[20];
char lonBuf[20];
void setup() {
// this is the data we want to appear in the advertisement
// (if the deviceName and advertisementData are too long to fix into the 31 byte
// ble advertisement packet, then the advertisementData is truncated first down to
// a single byte, then it will truncate the deviceName)
RFduinoBLE.advertisementData = "ledbtn";
// start the BLE stack
RFduinoBLE.begin();
Serial.begin(9600);//For GPS Communication
}
void loop(){
char c = byte(Serial.read());
gps.encode(c);
gps.get_position(&lat,&lon); // get latitude and longitude
// send position as char[]
String latString = String(lat);
String lonString = String(lon);
latString.toCharArray(latBuf, 20);
lonString.toCharArray(lonBuf, 20);
RFduinoBLE.send(lonBuf, 20);
}
void RFduinoBLE_onDisconnect()
{
}
void RFduinoBLE_onReceive(char *data, int len)
{
RFduinoBLE.send(lonBuf, 20);
}
One problem I see: the loop() is trying to read out the GPS coordinates every time loop is executed. This approach has two problems: 1) the loop doesn't wait until serial data is ready, and 2) the loop doesn't wait until the received GPS data is valid.
From reading http://arduino.cc/en/Tutorial/ReadASCIIString and http://arduiniana.org/libraries/tinygps/ I recommend rewriting loop() to something like this:
loop() {
char c;
float fLat, fLon;
unsigned long fix_age;
static unsigned long previous_fix_age = 0;
// If nothing to read; do nothing.
// Read as many characters as are available.
while (Serial.available() > 0) {
// Tell the GPS library about the new character.
c = Serial.read();
gps.encode(c);
gps.f_get_position(&flat, &flon, &fix_age);
if (fix_age != TinyGPS::GPS_INVALID_AGE && fix_age != previous_fix_age) {
// new GPS data is valid, new, and ready to be printed
previous_fix_age = fix_age; // remember that we've reported this data.
String latString = String(lat);
...the rest of the code you already have to print the lat and lon.
}
}
}
The code about previous_fix_age is there so that the loop prints coordinates only when a new fix has been received from the GPS.

Unwanted click when using SoXR Library to do variable rate resampling

I am using the SoXR library's variable rate feature to dynamically change the sampling rate of an audio stream in real time. Unfortunately I have have noticed that an unwanted clicking noise is present when changing the rate from 1.0 to a larger value (ex: 1.01) when testing with a sine wave. I have not noticed any unwanted artifacts when changing from a value larger than 1.0 to 1.0. I looked at the wave form it was producing and it appeared as if a few samples right at rate change are transposed incorrectly.
Here's a picture of an example of a stereo 440Hz sinewave stored using signed 16bit interleaved samples:
I also was unable to find any documentation covering the variable rate feature beyond the fifth code example. Here's is my initialization code:
bool DynamicRateAudioFrameQueue::intialize(uint32_t sampleRate, uint32_t numChannels)
{
mSampleRate = sampleRate;
mNumChannels = numChannels;
mRate = 1.0;
mGlideTimeInMs = 0;
// Intialize buffer
size_t intialBufferSize = 100 * sampleRate * numChannels / 1000; // 100 ms
pFifoSampleBuffer = new FiFoBuffer<int16_t>(intialBufferSize);
soxr_error_t error;
// Use signed int16 with interleaved channels
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
// "When creating a var-rate resampler, q_spec must be set as follows:" - example code
// Using SOXR_VR makes sense, but I'm not sure if the quality can be altered when using var-rate
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_HQ, SOXR_VR);
// Using the var-rate io-spec is undocumented beyond a single code example which states
// "The ratio of the given input rate and ouput rates must equate to the
// maximum I/O ratio that will be used: "
// My tests show this is not true
double inRate = 1.0;
double outRate = 1.0;
mSoxrHandle = soxr_create(inRate, outRate, mNumChannels, &error, &ioSpec, &qualitySpec, NULL);
if (error == 0) // soxr_error_t == 0; no error
{
mIntialized = true;
return true;
}
else
{
return false;
}
}
Any idea what may be causing this to happen? Or have a suggestion for an alternative library that is capable of variable rate audio resampling in real time?
After speaking with the developer of the SoXR library I was able to resolve this issue by adjusting the maximum ratio parameters in the soxr_create method call. The developer's response can be found here.

Communication between Arduino and Raspberry Pi over USB

I have an Arduino connected over USB with a Raspberry Pi. A Python program is sending a value over USB, then the arduino receives it and turns the LED on. The Arduino is sending a analogue value from A0 to the Python program on the Raspberry. But Python receives sometimes 024, 24 or 4 instead of 1024. How can I fix this? Here is my code:
http://www.bitsharr.com/Hsoxw7nG
Arduino Code:
int led = 13;
char charIn;
int sensorPin = A0; // select the input pin for the potentiometer
int sensorValue = 0; // variable to store the value coming from the sensor
long previousMillis = 0;
long interval = 1000;
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
Serial.begin(9600); //This initializes the USB as a serial port
}
void loop() {
if (Serial.available()) {
delay(5); // warten bis alle Daten da sind
while(Serial.available() > 0) {
charIn =(char) Serial.read();
if (charIn == '1') {
digitalWrite(led,HIGH);
delay(5000);
digitalWrite(led,LOW);
}
}
}
Python Code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from serial import Serial
ser = Serial('/dev/ttyUSB0', 9600)
x=ser.readline()
ser.write('1')
print(x)
def cleanup( str ):
result = ""
for c in str:
if( (c >= "0") and (c <= "9") ):
result += c
return result
print( cleanup(x))
#ser.write("1")
sensorValue = analogRead(sensorPin); //Reads the voltage of the resistor.
Serial.println(sensorValue); //Writes the voltage on the Serial port.
}
For starters, you are using Arduino code in your Python code.
sensorValue = analogRead(sensorPin); //Reads the voltage of the resistor.
Serial.println(sensorValue); //Writes the voltage on the Serial port.
How would your computer know what values are on your Arduino? You need to move this into the Arduino code. I can't tell you where: you never specified exactly what you want to accomplish with this code.
Some more problems with your code:
#ser.write("1") should be ser.write("1")
These two lines aren't bad but they're a bad coding practice to include because you don't use them:
long previousMillis = 0;
long interval = 1000;
But python receives sometimes 024, 24 or 4 instead of 1024.
The Arduino never sent anything; there is no serial.print(); or serial.println();! I don't know how you are receiving data. If you make the edits to your code above and/or specify exactly what you want to do (i.e. Send a 1 to the Arduino, turn a LED on, get sensor data back every five seconds, if the data is above 50%, turn the light off) and still don't know what to do, feel free to comment for clarification.