GPIO32 pin works in analog mode, always reads 0 in digital mode - esp32

I'm having some difficulty getting PCNT pulse counting working with a prototype ESP32 device board.
I have a water level sensor (model D2LS-A) that signals state by the frequency of a square wave signal it sends to GPIO32 (20Hz, 50Hz, 100Hz, 200Hz, 400Hz).
Sadly, the PCNT counter stays at 0.
To troubleshoot, I tried putting GPIO32 in ADC mode (attenuation 0, 10-bit mode) to read the raw signal (sampling it many times a second), and I'm getting values that I would expect (0-1023). But trying the same thing using digital GPIO mode, it always returns 0, never 1 in all the samples.
Since the PCNT ESP IDF component depends on reading the pin digitally, the counter never increments past 0.
So the real problem I'm having is: why aren't the ADC readings (between 0-1023) translating to digital readings of 0-1 as one would expect?

Related

Baud rate on Arduino

I am implementing a simple visible light communication module with two Arduinos, as a transmitter and a receiver, with a short text message consisting of 120 characters. I have used Manchester encoding with on-off -keying modulation.
Altogether, in my message frame, with Manchester encoding and with preambles and end-of-frame byte, there are 2480 bits. I have set one bit period to be 500 microseconds. On the receiver side, I am sampling this bit four times, at (500/4) 125 microseconds. From my knowledge, since each bit is 500 μs, there are 2000 bits/s that are being transmitted from the transmitter so a baud rate of 9600 bits/s should work. However, 9600 is not working and any baud rate above 38400 up to 115200 is working, and I can properly decode this short message in my receiver.
What is the explanation for this behavior? Why is baud rate of 9600 not working though I am only transmitting 2000 bits per second?
Further information: I have set the prescalar to 128, so the ADC sampling frequency is (16 MHz/128)/13 = 9.6 kHz.
When you suddenly started talking about "baud rates", it implied that you're using the hardware serial port on the Arduino. If so, then realise that feeding 2,000 bit/s into a device expecting 9,600 bit/s has problems.
The way that an asynchronous UART works is that it waits for a start signal (bit), then decodes the next (typically) 9 signals at the current bit rate. It then checks that the 9th bit is a stop bit; if it isn't, it discards the byte.
Since you're only changing every 9600/2000 = 4.8 bits, then odds on the 9th "stop" bit will be of the wrong sense, and the data will be lost.
Below is an ASCII diagram for the timing that I'm talking about.
I'll use the bitstream 00101101 for the signal produced by the circuit, with a . as a 0 ms separator between bits;
I'll use a ^ to point out where the UART is sampling the bits;
I'll use a * to indicate a "correct" byte (insofar as the byte ends in a correct stop bit);
I'll use a ! to indicate an "incorrect" byte (insofar as the byte ends in an incorrect stop bit);
Of course, I'll assume a baud rate of 10,000 bit/s (5 rather than 4.8...)
00000.00000.11111.00000.11111.11111.00000.11111
^^^^^.^^^^!.......^^^^^.^^^^*.......^^^^^.^^^^*
This sequence would result in the UART recording the following 3 bytes:
Error
0xF0 (LSB first is defined...)
0xF0 (LSB first is defined...)

Multiple PWM Channels on PIC

I use the PIC16F88 for my project, with XC8 compiler.
What I'm trying to achieve is to control 4 LEDs with 4 buttons, when you press a buttons it increases the duty cycle of the corresponding LED by 10%.
When you press the button on RB0 it increases the duty cycle of the LED on RB4, and so on.
Every LED is independent, therefore it can have a different duty cycle.
The problem is that the PIC i'm using only have one PWM module on either RB0 or RB3 (using CCPMX bit).
After some research I decided to implement software PWM to have four different channels, each channels would control one duty cycle, but most of the sources I found didn't provide any explanation on how to do it.
Or is there a way to mirror PWM to multiple pins ?
Thanks by advance for helping me out.
Mirroring is not an option.
PWM is relatively simple. You have to set PWM frequency (which you will not change) and PWM duty cycle (which you need to change in order to have 0-100% voltage range). You have to decide about resolution of PWM, voltage step that you need (built in PWM for example is 8-bit and has 0-255 steps).
Finally, you have to set timer to interrupt based on PWM frequency * PWM resolution. In Timer ISR routine you need to check resolution counts and PWM value of all your channels. Resolution count will have to reset when resolution value is reached (and start to count from 0 again, all outputs go HIGH here, also). When PWM value of output is reached you have to toggle (pull it LOW) corresponding pin (and reset it back to HIGH with every resolution count reset).
This is only one way of doing it, involves only one timer and should be most simple since your PIC is low with resources.
Hope it helps...

Real Time Workaround using windows for fixed sampling time

I am trying to collect data off an accelerometer sensor. I have an Arduino doing the analog to digital conversion of the signal and sending it through a serial port to MATLAB on Windows.
I send a reading every 5ms from the Arduino through the serial port. I am saving that data using MATLAB's serial read in a vector as well as the time at which it was read using the clock method.
If I was to plot the column of the vector where I have saved at which second I read, I get a curve (non-linear), and when I look at the difference between 1 read and another, I see that it is slightly varying.
Is there any way to get the data saved in real time with fixed sampling time?
Note: I am using 250000 baud rate.
Matlab Code:
%%%%% Initialisation %%%%%
clear all
clc
format shortg
cnt = 1;%File name changer
sw = 1;%switch: 0 we add to current vector and 1 to start new vector
%%%%% Initialisation %%%%%
%%%%% Communication %%%%%
arduino=serial('COM7','BaudRate',250000);
fopen(arduino);
%%%%% Communication %%%%%
%%%%% Reading from Serial and Writing to .mat file%%%%%
while true,
if sw == 0,
if (length(Vib(:,1))==1000),% XXXX Samples in XX minutes
filename = sprintf('C:/Directory/%d_VibrationReading.mat',cnt);
save (filename,'Vib');
clear Vib
cnt= cnt+1;
sw = 1;
end
end
scan = fscanf(arduino,'%f');
if isfloat(scan) && length(scan(:,1))==6,% Change length for validation
vib = scan';
if sw == 1,
Vib = [vib clock];
sw = 0;
else
Vib = [Vib;vib clock];
end
end
end
%%%%% Reading from Serial and Writing to .mat file%%%%%
% Close Arduino Serial Port
fclose(arduino);
Image 1 shows the data received through serial (each Row corresponding to 1 serial read)
Image 2 shows that data saved with the clock
Image 1:
Image 2:
I know that my answer does not contain a quick and easy solution. Instead it primarily gives advice how to redesign your system. I worked with real-time systems for several years and saw it done wrong too many time. It might be possible to just "fix", but working with your current communication pattern tweaking the performance but I am convinced you will never receive reliable time information.
I will answer this from a general system design perspective, instead of trying to fix your code. Where I see the problems:
In general, it is a bad idea to append time information on the receiving PC. Whenever the sensor is capable and has a clock, append the time information on the sensor system itself. This allows for an accurate relative timing between the measurements. Some clock adjustment might be necessary when the clock on the sensor is not set properly, but that is just a constant offset.
Switch from ASCII-encoded data to binary data. With your sample rate and baut rate set, you only have 50 bytes for each message.
Write a robust receiver. Just dropping messages you "don't understand" is not a good idea. Whenever the buffer is full, you might receive multiple messages unless you use a proper terminator.
Use preallocation. You know how large the batches you want to write are.
A simple solution for a message:
2 bytes - clock milliseconds
4 bytes - unix timestamp of measurement
For each sensor
2 bytes int32 sensor data
2 bytes - Terminator, constant value. Use a value which is outside the range for all previous integers, e.g. intmax
This message format should theoretically allow you to use 21 sensors. Now to the receiving part:
To get a first version running with a good performance, call fread (serial) with large batches of data (size parameter) and dump all readings into a large cell array. Something like:
C=cell(1000,1)
%seek until you hit a terminator
while not(terminator==fread(arduino,1));
for ix=1:numel(C)
C{ix}=fread(arduino,'int16',1000)
end
fclose(arduino);
Once you read the data append it to a single vector: C=[C{:}]; and try to parse it in post-processing. If you manage the performance you may later return to on-the-fly processing, but I recommend to start this way to get the system established.

IR emitter and PWM output

I have been using FRDM_KL46Z development board to do some IR communication experiment. Right now, I got two PWM outputs with same setting (50% duty cycle, 38 kHz) had different voltage levels. When both were idle, one was 1.56V, but another was 3.30V. When the outputs were used to power the same IR emitter, the voltages were changed to 1.13V and 2.29V.
And why couldn't I use one PWM output to power two IR emitters at the same time? When I tried to do this, it seemed that the frequency was changed, so two IR receivers could not work.
I am not an expert in freescale, but how are you controlling your pwm? I'm guessing each pwm comes from a separate timer, maybe they are set up differently. Like one is in 16 bit mode (the 3.3V) and the other in 32 (1.56v) in that case even if they have the same limit in the counter ((2^17 - 1) / 2) would be 50% duty cycle of a 16 bit timer. But in a 32 bit, that same value would only be 25% duty so, one output would be ~1/2 the voltage of the other. SO I suggest checking the timer setup.
The reason the voltage changed is because the IR emmiters were loading the circuit. In an ideal situation this wouldn't happen, but if a source is giving too much current the voltage usually drops a bit.

what is the reason for unequal voltages in different ports in pwm?

I have a piece of code for getting same analog pwm output voltage from PB4 and PB5 using fast pwm in output compare mode. However the voltage from them is different. What could possibly be the reason for this ? Also the voltage from neither of the pins is close to 1.23 V which is what should be the output voltage should be.
Here is the code.
#include <`avr/io.h`>
#include <`avr/interrupt.h`>
ISR(TIMER0_COMP_vect)
{
cli();
PORTB &= ~(1<<PB5);
sei();
}
ISR(TIMER0_OVF_vect)
{
cli();
PORTB |= (1<<PB5);
sei();
}
void init(void)
{
TCCR0 |= (0<<FOC0)|(1<<WGM01)|(1<<WGM00)|(1<<COM01)|(1<<COM00)|(1<<CS02)|(1<<CS01)|(1<<CS00);
OCR0 = 63;
TIMSK |= (1<<OCIE0)|(1<<TOIE0);
}
int main(void)
{
DDRB = 0xFF;
PORTB = 0xFF;
init();
sei();
while(1);
}
Firstly, if you're using something like an ATMega328p, having all three CS bits set will enable an external clock source, rather than using the internal clock, and so the timer won't run (unless you do actually have an external timer clock source). Depending on what microcontroller you are using, make sure that those bits are enabling a particular prescaler value instead.
Secondly, you might also encounter problems due to your measurement method and the way PWM actually works. Though it is often listed as an analog output when dealing with Arduinos, pulse width modulation actually does exactly what it says - it rapidly switches a digital output between ground and VCC (likely 5V), with a varying duty cycle. If one of those output pins is viewed on an oscilloscope, it will probably show some form of square wave.
When measured with a multimeter, the value you are seeing will be a combination of samples taken while the output is either high or low, and possibly an average of these randomly timed samples, hence the unexpected reading.
To get your desired result, you really need to smooth out the digital output. In short, this is often done with a low pass filter, composed of a resistor and capacitor attached to the output pin.
This works by using the square wave to charge the capacitor through the resistor while it is high, and discharge it while it is low. By having more time high than low (a longer duty cycle), the capacitor stabilises at a higher voltage (and vice versa). The resistor limits the current drawn from the AVR output pin (as if the capacitor was at 0V and the output gets driven high, you're effectively shorting the output to ground momentarily).
For your case, a resistor somewhere around 4.7K and a capacitor around 2uf will probably suit. Increase the capacitance or the resistance to reduce ripple.

Resources