I'm using a RedBearLabs Blend V2 to communicate with a SPI peripheral. Everything is functioning correctly at this point and I am receiving the expected data using SPI.write() and catching the return value. The SPI bus is running at 1 MHz in mode 0.
The image below shows the SPI bus connected to a scope. Trace 1 is SCLK, 2 is MISO, 3 is MOSI and 4 is CS. As you can see, I have 4 bursts of SCLK whilst CS is low, each 8 bits in length with a delay of approximately 20 µs in between each burst. Ideally, I need to completely alleviate this 20 µs delay and have 1 SCLK burst of 32 cycles.
The below code is how I am currently achieving what is seen in the scope grab.
int16_t MAX1300::sampleChannel(uint8_t channel, uint8_t inputMode, uint8_t adcMode)
{
int16_t sample;
int8_t hi = 0;
int8_t lo = 0;
MAX1300::InputWord_u word;
word.bits.start = 0b1;
if (inputMode == MAX1300::SINGLE_ENDED) {
word.bits.select = (Channels_se_e)channel;
} else {
word.bits.select = (Channels_dif_e)channel;
}
word.bits.payload = 0b0000;
if (adcMode == MAX1300::EXT_CLK) {
m_cs = 0;
m_spiBus.write(word.all);
m_spiBus.write(7);
hi = m_spiBus.write(0);
lo = m_spiBus.write(0);
m_cs = 1;
}
sample = ((int16_t)hi << 8) | lo;
return sample;
}
So far I have tried setting SPI.format(16, 0) with the intention of having 2 SCLK bursts of 16 cycles, however the SPI bus no longer functions if I do this. The same happens if I use SPI.transfer() with 32-bit buffers - no SPI bus.
I am able to increase the frequency of the bus, thus reducing the delay between each SCLK burst, however this is not really a suitable solution due to the end-application for this device.
What am I doing wrong here, or is what I am attempting to do just not possible with this hardware/firmware combination?
Thanks, Adam
Related
I'm reading on a stream connected on a UART serial port via half-duplex RS-485 protocol at 9600bps, Data : 8 bits, Stop 1 bit no parity with an embedded device.
I know that the system which I'm connected to sends binary commands between 2 byte and 10 bytes long at an interval of 20ms.
I access the stream with the following flags:
uart0_filestream = open(COM_UART_DEV, O_RDWR | O_NOCTTY | O_NDELAY);
However, it happens frequently that the 10 bytes long commands will be chunked in half causing a checksum error in my application. I need to poll every 20 ms and the only solution I found for this is to inscrease the sleep time between polls, which I don't want.
Is there a flag or a smart method that I can use to make sure the transmission is complete before reading the content of the stream buffer?
Okay, I found a solution that's ok for my needs. Since I can't know for sure that when I will read the content of the stream all the data will be there and I don't want to increase my poll interval, as #sawdust suggested, I increased the poll rate:
unsigned char *pStartBuffer = pRxBuffer;
if(uart0_filestream != -1)
{
int rx_length = 0, rx = 0, elapsed = 0;
bool bCommand = false;
while(rx_length <= 10)//&& elapsed <= 10)
{
rx = read(uart0_filestream, (void*)pRxBuffer, length);
if(rx > 0)
{
rx_length += rx;
pRxBuffer += rx;
if(checksum(pStartBuffer, rx_length) == true)
{
bCommand = true;
break;
}
}
nanosleep(&sleep_rx, NULL);
//elapsed+=2;
}
I increased the poll rate to 8ms at first. Since I know that the longest command I can receive is 10 bytes long, I read until the checksum is valid or that the content read is 10 bytes long and sleep an extra 2ms between polls. This performs very well for now.
I am using an Arduino uno to measure the speed of a dc motor.
I have a opto sensor that gives a pulse when the motor has made a full turn.
The problem I've got starts when the motor has a speed > 90Hz.
As soon as I reach 90Hz, the Arduino doesn't enter the interrupt function.
My code:
int pin = 13;
volatile int state = LOW;
volatile unsigned long startTijd = 0;
volatile unsigned long eindTijd = 0;
unsigned int frequentie = 0;
volatile int count = 0;
void setup()
{
pinMode(pin, OUTPUT);
attachInterrupt(0, blink, FALLING); //LOW, HIGH, FALLING, RISING, CHANGE
Serial.begin(19200);
}
void loop()
{
noInterrupts();
digitalWrite(pin, state);
interrupts();
}
void blink()
{
if (count == 0) {
startTijd = micros();
}
count++;
if (count == 31) {
count = 0;
eindTijd = micros();
eindTijd -= startTijd;
Serial.print(eindTijd);
Serial.print(" ms. - ");
frequentie = 30 * 1000000 / eindTijd;
Serial.print(frequentie);
Serial.println(" Hz.");
}
state = !state;
}
My question is : When the Arduino receives interrupts at 90Hz, it doesn't execute the code in the interrupt. When the motor goes below 90Hz after that, the code works again. What am I doing wrong ?
It looks as though blink is your ISR. If that's the case, you shouldn't be doing debug I/O within that routine for 2 reasons. The first is that you are calling a process that could block. The second is that ISRs should do their thing and finish (should be highly efficient). My guess is that if you remove the debug I/O from your ISR and pass info back to the interrupted task instead (safely, of course) you will be able to service interrupts at greater than 90 hz.
Just to add to #Bruce answer. You are using serial IO in the interrupt. Each time you are printing around 15-20 characters (depending on the values calculated). Each character is encoded by 8 bit data + 1 start bit + 1 stop bit = 10 bits. So, say 20*10=200 bits. The baud rate is 19200bps, so time required to transmit 200 bits is 200/19200 sec, or in terms of frequency 19200/200=96Hz. So this is the maximum frequency achievable for transmission of 20 characters, which is close to your measured 90Hz (take in account that I am not considering any time spacing overhead between the transmits).
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 achieve that with an Arduino Uno board (ATmega328, 16 MHz). So I searched through the Internet and came up with something like this:
unsigned long Time=0;
int main (void)
{
Serial.begin(9600);
cli();
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 15999; // Compare value
TCCR1B |= (1 << WGM12)| (1 << CS10); // Prescaler
TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt
sei();
while(1) {
Serial.println(TCNT1);
}
return 0;
}
ISR(TIMER1_COMPA_vect)
{
Time++;
Serial.println(Time);
}
I'm trying to achieve a frequency of 1 kHz, so I'll be able to create intervals which are a couple of milliseconds long.
That's why I chose the comparison value to be 15999 (so 16000-1) and the prescaler to be equal to 1, so I get (at least what I believe to be the right calculation):
Frequency = 16.000.000 MHz/16000 = 1000 Hz = 1 kHz
The problem now is that, even though the Serial.println(TCNT1) shows me numbers counted up to 16000, back to zero, up to 16000, back to zero,..., Serial.println(Time) just counts up to 8, and it just stops counting although TCNT1 is still counting.
I thought about some kind of overflow somewhere, but I could not think about where; the only thing I came up with is that the comparison value might be too big which is -as I think - obviously not the case since 2^16 -1=65.535>15999.
If I, for instance, make the prescaler, let's say 64, and leave the comparison value, Time counts as expected. So I'm wondering: Why does ISR() stops getting called at a value of 8, but works when bringing up the prescaler?
I'm not sure, but depending on the version of Arduino you use, the println call would be blocking. If you call it faster than it can complete in your ISR, the stack will overflow.
If you want higher resolution timing, maybe try differencing the getMicroseconds result in your Loop(). You should cycle in Loop() far faster than once per millisecond.
If you want to do something once per millisecond, capture a start microseconds, and then subtract it from the current microseconds in a conditional in your Loop() function. When you see more than 1000 do the task...
It seems like the resolution of the timer was too much for my Arduino Uno (16 MHz). Chosing a lower resolution (i.e higher compare value) fixed the issue for me.
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.