I am using an hyperspectral sensor from Hamamatsu (C12880MA). So far, I got my firmware done to fit the Atmega328p, timings etc. are working great.
But now, I bumped into some issues regarding reading the measurements off the ADC.
According to my Clock-Signal generation (at variable f between 0.5 and 5 MHz), I need to read off the ADC values at exact "flag" values.
I am using the internal TC1 timer for clock generation. At each toggle of the CLK signal, I set a flag via an ISR to time the other signals / restart the program.
Now to the problem: I know (see datasheet for ref), that the VIDEO Signal of the sensor will appear between the flags "ADC_start" and "ADC_End" at the pace of the generated CLK. I have to read the values through the internal Arduino ADC at exactly the correct flag, to later match with the correct underlaying wavelengths.
Here are the essential snippets of the code:
volatile uint16_t flag = 0;
uint16_t data[288]; // define 1d matrix for data storage
uint16_t ST_Start = 2; // starter flag for the programm
uint16_t ST_End = 134; // end flag for ST signal
uint16_t ADC_Start = 310; //starter flag for ADC readout
uint16_t ADC_End = 886; //end flag for ADC readout
uint16_t index; //index used for data storage
volatile uint16_t End = 1000; // end flag for programm restart and clearing cache
The ISR handles increasing the flag on every CPU clock..
ISR(TIMER1_COMPA_vect){
if (flag <= End){
flag = flag+1;
} else
{
flag = 0;
//delayMicroseconds(2000);
}
this is my readData function. Unfortunately I have not found the correct way to read the ADC values at the exact flags needed. Say I need to read values at the flags 300, 302, 304, ..., 900 (on every second flag, between an set interval; ADC_Start to ADC_End)
void readData() {
if (flag >= ADC_Start && flag <= ADC_End ){
for (uint16_t i = ADC_Start; i < ADC_End; i = i+2)
{
index = i - ADC_Start;
data[index] = analogRead(VID);
}
} else { }
}
Here´s the data sheet: https://www.hamamatsu.com/resources/pdf/ssd/c12880ma_kacc1226e.pdf
See p.7 for timings.
Thanks!
Related
I am using STM32F769 Disc board with Mbed online compiler. My task is to change the PWM frequency and duty ratio according to input.
I've created a simple algorithm according to my need, the program is working well but whenever the controller updates the PWM frequency, there is strange behavior(overlapped maybe, difficult to explain verbally for me), the frequency is changed instantly and the output is incorrect at that moment. For other controllers (like arduino) this never happens, the controller updates value after the time period of PWM is over. But not in this case.
What can be wrong?
I thought to add a small delay before value is updated but that will not work, as every time a different delay would be needed. I have attached the code and screenshots.
#include "mbed.h"
AnalogIn analog_value(A0);
PwmOut pulse(D11);
int main() {
double meas_v = 0;
double out_freq, out_duty, s_time;
while (1) {
meas_v = analog_value.read() * 3300;
if (meas_v < 1) {
out_freq = 50000;
out_duty = 40;
} else if (meas_v >= 1000) {
out_freq = 100000;
out_duty = 80;
} else {
out_freq = 50000 + (meas_v * 50);
out_duty = 40 + (meas_v * 0.04);
}
pulse.period(1.0 / out_freq);
pulse = out_duty / 100;
s_time = 0.0001;
wait(s_time);
}
}
The output should be updated after the current period is completed, not instantly.
Error I am getting
The underlying HAL code probably resets the current count value of the timer when you set the new period. You'll have to read the current timer cnt value and wait for it to reach 0. You can set a new period when the timer cnt value reaches 0.
You need to update the value in the update interrupt or use the timer DMA burst mode
I simply want to continuously calculate the frequency of a sine signal with a comparator input (on the falling edges). The effective target frequency is about ~122 Hz and my implementation works most the time, but sometimes it calculates a wrong frequency with always about ~61Hz (which cannot be possible, I verified this with an oscilloscope).
It seems my implementation has a weakness, perhaps in form of a race condition or misuse of the timer, since it uses concurrent interrupt service routines and manually starts and stops the timer.
I also think the bug correlates with the measured frequency of about ~122Hz, because one timer overflow is pretty much the same:
One Timer Overflow = 1 / (1/8 MHz * 2^16 [Bits]) = 122.0703125 Hz
I am using a 8051 microcontroller (Silicon Labs C8051F121) with the following code:
// defines
#define PERIOD_TIMER_FREQ 8000000.0 // Timer 3 runs at 8MHz
#define TMR3_PAGE 0x01 /* TIMER 3 */
#define CP1F_VECTOR 12 /* comparator 1 falling edge */
#define TF3_VECTOR 14 /* timer3 reload timer */
sfr TMR3CN = 0xC8; /* TIMER 3 CONTROL */
sfr TMR3L = 0xCC; /* TIMER 3 LOW BYTE */
sfr TMR3H = 0xCD; /* TIMER 3 HIGH BYTE */
// global variables
volatile unsigned int xdata timer3_overflow_tmp; // temporary counter for the current period
volatile unsigned int xdata timer3_lastValue; // snapshot of the last timer value
volatile unsigned int xdata timer3_overflow; // current overflow counter, used in the main routine
volatile unsigned int xdata tempVar; // temporary variable
volatile unsigned long int xdata period; // the caluclated period
volatile float xdata period_in_SI; // calculated period in seconds
volatile float xdata frequency; // calculated frequency in Hertz
// Comparator 1 ISR has priority "high": EIP1 = 0x40
void comp1_falling_isr (void) interrupt CP1F_VECTOR
{
SFRPAGE = TMR3_PAGE;
TMR3CN &= 0xfb; // stop timer 3
timer3_lastValue = (unsigned int) TMR3H;
timer3_lastValue <<= 8;
timer3_lastValue |= (unsigned int) TMR3L;
// check if timer 3 overflow is pending
if (TMR3CN & 0x80)
{
timer3_overflow_tmp++; // increment overflow counter
TMR3CN &= 0x7f; // Clear over flow flag. This will also clear a pending interrupt request.
}
timer3_overflow = timer3_overflow_tmp;
// Reset all the timer 3 values to zero
TMR3H = 0;
TMR3L = 0;
timer3_overflow_tmp = 0;
TMR3CN |= 0x04; // restart timer 3
}
// Timer 3 ISR has priority "low", which means it can be interrupted by the
// comparator ISR: EIP2 = 0x00
// Timer 3 runs at 8MHz in 16 bit auto-reload mode
void timer3_isr(void) interrupt TF3_VECTOR using 2
{
SFRPAGE = TMR3_PAGE;
timer3_overflow_tmp++;
TMR3CN &= 0x7f; // Clear over flow flag. This will also clear a pending interrupt request.
}
void main(void)
{
for(;;)
{
...
calcFrequencyLabel: // this goto label is a kind of synchronization mechanism
// and is used to prevent race conditions caused by the ISRs
// which invalidates the current copied timer values
tempVar = timer3_lastValue;
period = (unsigned long int)timer3_overflow;
period <<= 16;
period |= (unsigned long int)timer3_lastValue;
// If both values are not equal, a race condition has been occured.
// Therefore the the current time values are invalid and needs to be dropped.
if (tempVar != timer3_lastValue)
goto calcFrequencyLabel;
// Caluclate period in seconds
period_in_SI = (float) period / PERIOD_TIMER_FREQ;
// Caluclate period in Hertz
frequency = 1 / period_in_SI; // Should be always stable about ~122Hz
...
}
}
Can someone please help me to find the bug in my implementation?
I can't pin-point the particular bug, but you have some problems in this code.
The main problem is that the 8051 was not a PC, but rather it was the most horrible 8-bit MCU to ever become mainstream. This means that you should desperately avoid things like 32 bit integers and floating point. If you disassemble this code you'll see what I mean.
There is absolutely no reason why you need to use floating point here. And 32 bit variables could probably be avoided too. You should use uint8_t whenever possible and avoid unsigned int too. Your C code shouldn't need to know the time in seconds or the frequency in Hz, but just care about the number of timer cycles.
You have multiple race condition bugs. Your goto hack in main is a dirty solution - instead you should prevent the race condition from happening in the first place. And you have another race condition between the ISRs with timer3_overflow_tmp.
Every variable shared between an ISR and main, or between two different ISR with different priorities, must be protected against race conditions. This means that you must either ensure atomic access or use some manner of guard mechanism. In this case, you could probably just use a "poor man's mutex" bool flag. The other alternative is to change to an 8 bit variable and write the code accessing it in inline assembler. Generally, you cannot have atomic access on an unsigned int on a 8-bit core.
With a slow edge as you would have for low frequency sine and insufficient hysteresis in the input (the default being none), it would only take a little noise for a rising edge to look like a falling edge and result in half the frequency.
The code fragment does not include the setting of CPT1CN where the hysteresis is set. For your signal you probably need to max it out, and ensure that the peak-to-peak noise on your signal is less that 30mV.
Can someone please help me with code? I have a 24 teeth trigger wheel. Every tooth is registered by hall sensor and I need that Arduino simulate 36 pulse output of that corresponding 24 pulse input.
Here is my code with delayMicroseconds, but I can`t use delayMicroseconds, because Arduino doesn't understand bigger than 16k micros delay.
const int hall = 2; // hall sensor
const int ledPin = 13; // the pin that the LED is attached to
// Variables will change:
int teethCounter = 0;
int hallState = 0;
int lasthallState = 0;
long cycles=0;
boolean cycle = false;
unsigned long microsStart = 0;
unsigned long microsStop = 0;
unsigned long usElapsed = 0;
unsigned long usElapsedUp = 0;
unsigned long usInterval;
void setup() {
// initialize the button pin as a input:
pinMode(hall, INPUT);
// initialize the LED as an output:
pinMode(ledPin, OUTPUT);
// initialize serial communication:
Serial.begin(9600);
}
void loop() {
hallState = digitalRead(hall);
if(cycle==true){
microsStart=micros();
}
if(cycle==true){
usInterval = usElapsedUp/72;
for (int i=0; i <= 36; i++){
digitalWrite(13,HIGH);
delayMicroseconds(usInterval);
digitalWrite(13,LOW);
delayMicroseconds(usInterval);
cycle = false;
}
}
// compare the hallState to its previous state
if (hallState != lasthallState) {
// if the state has changed, increment the counter
if (hallState == HIGH) {
teethCounter++;
if(teethCounter==24){
cycle = true;
cycles++;
teethCounter=0;
usElapsedUp = usElapsed;
}
Serial.print("Tooth count: ");
Serial.print(teethCounter);
Serial.print(" Cycles: ");
Serial.print(cycles);
Serial.print(" Time: ");
Serial.print(usElapsedUp);
Serial.print(" Interval: ");
Serial.println(usInterval);
}
microsStop=micros();
usElapsed=microsStop-microsStart;
}
// save the current state as the last state,
//for next time through the loop
lasthallState = hallState;
}
How can I calculate and from where can I take trigger points?
If(event happens==true){
digitalWrite(13,HIGH);
}
If(event happens==false){
digitalWrite(13,LOW);
}
If it helps to understand here is a block diagram
As long as you understand that you will never be able get 36 pulses per turn accuracy with 24 pulse/turn, you can do this, which is a common trick derived from the Bresenham algorithm. This solution assumes you are concerned about the position.
Now, this will generate pulses in real-time, as opposed to your code, which generates pulses in a blocking manner, I don't think losing pulses was your original intent.
This code will not generate pulses uniformly, 1 out of 3 readings will generate 2 pulses.
Another way would be to calculate the average speed and program a hardware timer to simulate the 36 pulses per turn, using interrupts, but going that route would likely (invariably, in my experience) end up in total loss of sync between the actual position of the wheel and what your corrected tick count reports. There are also strict speed ranges that you have to respect if going that route, also this will introduce severe latency issues to your application.
Change the increment value to 36, and the whole turn count to 24/36.
Change the step detection to a threshold of 24.
I'm trying to understand why you want to do this 36/24 thing, and can't.
So, your mileage may vary.
// compare the hall State to its previous state
// to declared outside of loop()
// int smallCounter;
// PULSE_WIDTH as small positive pulse with in us
//
if (hallState != lasthallState) {
// if the state has changed, increment the counter
smallCounter += ((hallState == HIGH) ? 36 : 0);
// ... I'm assuming that the serial prints you had here were just here for debugging.
lasthallState = hallState;
}
//
// reporting for each step below
//
if (smallCounter >= 24)
{
smallCounter -= 24;
if (++teethCounter >= 36) {
cycle = true;
cycles++;
teethCounter=0;
usElapsedUp = usElapsed;
}
digitalWrite(13,HIGH);
delayMicroseconds(PULSE_WIDTH);
digitalWrite(13,LOW);
delayMicroseconds(PULSE_WIDTH); // this is probably not needed.
}
I'm dealing with arduino mega based quadcopter and trying to make PWM frequency for 4 motors - 400hz each. I've found an interesting solution where 4 ATmega2560 16bit timers are used to control 4 ESCs with PWM so it could reach 400hz frequency. 700 to 2000µs are normal pulse widths ESC are dealing with.
1sec/REFRESH_INTERVAL = 1/0.0025 = 400hz.
this is servo.h lib:
#define MIN_PULSE_WIDTH 700 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2000 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1000 // default pulse width when servo is attached
#define REFRESH_INTERVAL 2500 // minimum time to refresh servos in microseconds
#define SERVOS_PER_TIMER 1 // the maximum number of servos controlled by one timer
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
The problem is to make it work each PWM should be controlled with 1 16bit timer. Otherwize, say, 2 escs on 1 timer would give 200hz. So all of 16bit timers are busy controlling 4 ESC but I still need to read input PPM from receiver. To do so I need at least one more 16bit timer which I don't have anymore. It's still one 8bit timer free bit it can only read 0..255 numbers while normal number escs operate with are 1000..2000 and stuff.
So what would happen if I'll use same 16bit timer for both pwm and ppm reading? Would it work? Would it decrease speed drastically? I have arduino working in pair with Raspberry Pi which controls data filtering, debugging, and stuff, is it better to move ppm reading to Raspberry?
To answer one of your questions:
So what would happen if I'll use same 16bit timer for both pwm and ppm
reading? Would it work?
Yes. When your pin change interrupt fires you may just read the current TCNT value to find out how long it has been since the last one. This will not in any way interfere with the timer's hardware PWM operation.
Would it decrease speed drastically?
No. PWM is done by dedicated hardware, software operations running at the same time will not affect its speed and neither will any ISRs you may have activated for the corresponding timer. Hence, you can let the timer generate the PWM as desired and still use it to a) read the current counter value from it and b) have an output compare and/or overflow ISR hooked to it to create a software-extended timer.
Edit in response to your comment:
Note that the actual value in the TCNT register is the current timer (tick) count at any moment, irrespective of whether PWM is active or not. Also, the Timer OVerflow interrupt (TOV) can be used in any mode. These two properties allow to make a software-extended timer for arbitrary other time measurement tasks via the following steps:
Install and activate a timer overflow interrupt for the timer/counter you want to use. In the ISR you basically just increment a (volatile!) global variable (timer1OvfCount for example), which effectively counts timer overflows and thus extends the actual timer range. The current absolute tick count can then be calculated as timer1OvfCount * topTimerValue + TCNTx.
When an event occurs, e.g a rising edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount and store these values in another global variable (e.g. startTimestamp), effectively starting your time measurement.
When the second event occurs, e.g. a falling edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount. Now you have the timestamp of the start of the signal in startTimestamp and the timestamp of the end of the signal in another variable. The difference between these two timestamps is exactly the duration of the pulse you're after.
Two points to consider though:
When using phase-correct PWM modes the timer will alternate between counting up and down successively. This makes finding the actual number of ticks passed since the last TOV interrupt a little more complicated.
There may be a race condition between one piece of code first reading TCNT and then reading timer1OvfCount, and the TOV ISR. This can be countered by disabling interrupts, then reading TCNT, then reading timer1OvfCount, and then checking the TOV interrupt flag; if the flag is set, there's a pending, un-handled overflow interrupt -> enable interrupts and repeat.
However, I'm pretty sure there are a couple of library functions around to maintain software-extended timer/counters that do all the timer-handling for you.
what is unit of 700 and 2000?I guess usec.You have not exaplained much in your question but i identified that you need pulses of 25msec duration in which 700 usec on time may be 0 degree and 2000 may be for 180 degree now pulse input of each servo may be attached with any GPIO of AVR.and this GPIOs provide PWM signal to Servo.so i guess you can even control this all motors with only one timer.With this kind of code:
suppose you have a timer that genrate inturrupt at every 50 usec.
now if you want 700 usec for motor1,800 usec for motor 2,900 usec for motor 3 & 1000 usec for motor 4 then just do this:
#define CYCLE_PERIOD 500 // for 25 msec = 50 usec * 500
unsigned short motor1=14; // 700usec = 50x14
unsigned short motor2=16; // 800usec
unsigned short motor3=18; // 900usec
unsigned short motor4=20; // 1000usec
unsigned char motor1_high_flag=1;
unsigned char motor2_high_flag=1;
unsigned char motor3_high_flag=1;
unsigned char motor4_high_flag=1;
PA.0 = 1; // IO for motor1
PA.1 = 1; // IO for motor2
PA.2 = 1; // IO for motor3
PA.3 = 1; // IO for motor4
void timer_inturrupt_at_50usec()
{
motor1--;motor2--;motor3--;motor4--;
if(!motor1)
{
if(motor1_high_flag)
{
motor1_high_flag = 0;
PA.0 = 0;
motor1 = CYCLE_PERIOD - motor1;
}
if(!motor1_high_flag)
{
motor1_high_flag = 1;
PA.0 = 1;
motor1 = 14; // this one is dummy;if you want to change duty time update this in main
}
}
if(!motor2)
{
if(motor2_high_flag)
{
motor2_high_flag = 0;
PA.1 = 0;
motor2 = CYCLE_PERIOD - motor2;
}
if(!motor2_high_flag)
{
motor2_high_flag = 1;
PA.1 = 1;
motor2 = 16;
}
}
if(!motor3)
{
if(motor3_high_flag)
{
motor3_high_flag = 0;
PA.2 = 0;
motor3 = CYCLE_PERIOD - motor3;
}
if(!motor3_high_flag)
{
motor3_high_flag = 1;
PA.2 = 1;
motor3 = 18;
}
}
if(!motor4)
{
if(motor4_high_flag)
{
motor4_high_flag = 0;
PA.3 = 0;
motor4 = CYCLE_PERIOD - motor4;
}
if(!motor4_high_flag)
{
motor4_high_flag = 1;
PA.3 = 1;
motor4 = 19;
}
}
}
& tell me what is ESC?
I'm trying to establish a SerialPort connection which transfers 16 bit data packages at a rate of 10-20 kHz. Im programming this in C++/CLI. The sender just enters an infinte while-loop after recieving the letter "s" and constantly sends 2 bytes with the data.
A Problem with the sending side is very unlikely, since a more simple approach works perfectly but too slow (in this approach, the reciever sends always an "a" first, and then gets 1 package consisting of 2 bytes. It leads to a speed of around 500Hz).
Here is the important part of this working but slow approach:
public: SerialPort^ port;
in main:
Parity p = (Parity)Enum::Parse(Parity::typeid, "None");
StopBits s = (StopBits)Enum::Parse(StopBits::typeid, "1");
port = gcnew SerialPort("COM16",384000,p,8,s);
port->Open();
and then doing as often as wanted:
port->Write("a");
int i = port->ReadByte();
int j = port->ReadByte();
This is now the actual approach im working with:
static int values[1000000];
static int counter = 0;
void reader(void)
{
SerialPort^ port;
Parity p = (Parity)Enum::Parse(Parity::typeid, "None");
StopBits s = (StopBits)Enum::Parse(StopBits::typeid, "1");
port = gcnew SerialPort("COM16",384000,p,8,s);
port->Open();
unsigned int i = 0;
unsigned int j = 0;
port->Write("s"); //with this command, the sender starts to send constantly
while(true)
{
i = port->ReadByte();
j = port->ReadByte();
values[counter] = j + (i*256);
counter++;
}
}
in main:
Thread^ readThread = gcnew Thread(gcnew ThreadStart(reader));
readThread->Start();
The counter increases (much more) rapidly at a rate of 18472 packages/s, but the values are somehow wrong.
Here is an example:
The value should look like this, with the last 4 bits changing randomly (its a signal of an analogue-digital converter):
111111001100111
Here are some values of the threaded solution given in the code:
1110011001100111
1110011000100111
1110011000100111
1110011000100111
So it looks like the connection reads the data in the middle of the package (to be exact: 3 bits too late). What can i do? I want to avoid a solution where this error is fixed later in the code while reading the packages like this, because I don't know if the the shifting error gets worse when I edit the reading code later, which I will do most likely.
Thanks in advance,
Nikolas
PS: If this helps, here is the code of the sender-side (an AtMega168), written in C.
uint8_t activate = 0;
void uart_puti16(uint16_t val) //function that writes the data to serial port
{
while ( !( UCSR0A & (1<<UDRE0)) ) //wait until serial port is ready
nop(); // wait 1 cycle
UDR0 = val >> 8; //write first byte to sending register
while ( !( UCSR0A & (1<<UDRE0)) ) //wait until serial port is ready
nop(); // wait 1 cycle
UDR0 = val & 0xFF; //write second byte to sending register
}
in main:
while(1)
{
if(active == 1)
{
uart_puti16(read()); //read is the function that gives a 16bit data set
}
}
ISR(USART_RX_vect) //interrupt-handler for a recieved byte
{
if(UDR0 == 'a') //if only 1 single data package is requested
{
uart_puti16(read());
}
if(UDR0 == 's') //for activating constant sending
{
active = 1;
}
if(UDR0 == 'e') //for deactivating constant sending
{
active = 0;
}
}
At the given bit rate of 384,000 you should get 38,400 bytes of data (8 bits of real data plus 2 framing bits) per second, or 19,200 two-byte values per second.
How fast is counter increasing in both instances? I would expect any modern computer to keep up with that rate whether using events or directly polling.
You do not show your simpler approach which is stated to work. I suggest you post that.
Also, set a breakpoint at the line
values[counter] = j + (i*256);
There, inspect i and j. Share the values you see for those variables on the very first iteration through the loop.
This is a guess based entirely on reading the code at http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx#Y228. With this caveat out of the way, here's my guess:
Your event handler is being called when data is available to read -- but you are only consuming two bytes of the available data. Your event handler may only be called every 1024 bytes. Or something similar. You might need to consume all the available data in the event handler for your program to continue as expected.
Try to re-write your handler to include a loop that reads until there is no more data available to consume.