Using the register of an Arduino Mega 2560, I am trying to grab the information of the PORTA. I have referred to the datasheet (pages 69-72) and understood that I've to use PINxn (PINA) for this. But all I am getting is 0 as output. I have connected the pin to a LED.
The code and the output are mentioned below.
CODE
#define F_CPU 16000000
#include <avr/io.h>
int main(void) {
DDRA = (1 << DDA0); // sets the pin OUTPUT
__asm__("nop\n\t");
PORTA = 0x01; // Sets it HIGH
unsigned int i = PINA;
Serial.println(i);
}
OUTPUT
0
Thanks in advance for your time – if I’ve missed out anything, over- or under-emphasised a specific point let me know in the comments.
If you want to read back the value previously written to output, I recommend to read it from the register you wrote to, i.e. PORTA.
However according to provided docu (bold by me):
13.2.4 Independent of the setting of Data Direction bit DDxn, the port pin can be read through the PINxn Register bit.
A possible explanation for reading back the old value, immediatly after writing a different one, is probably the shortly following part in the same chapter:
PINxn Register bit and the preceding latch constitute a synchronizer. This is
needed to avoid metastability if the physical pin changes value near the edge of the internal clock, but it also introduces
a delay.
So you will have to account for that delay.
Have a look at timing features provided e.g. by available libraries and at available timer hardware.
But as a proof of concept, I propose to demonstrate by
print the value of PINA before writing the inverted value
write the inverted value to PORTA (inverting only the relevant bit of course)
read and print the value of PINA afterwards (hoping that your header uses volatile here) many times (say 1000)
I expect that you will see several old values, but then the new value.
Depending on how the printing is done (busy waiting?), once might be sufficient.
Your NOP (__asm__("nop\n\t");) might be designed to do the appropriate waiting. But I think it is misplaced (should be after writing new value) and it might be too short. If it is from example code, it should be sufficient. Move it, and maybe do it twice, to be sure for first try. That is likely to be effective.
You should put the "nop" in between the "PORTA = " assignment and "PINA" read. Because the instruction of writing to the PORTx register updates the status of the output pins just at the end of the system clock cycle at the rising edge of the clock generator, but reading from the PINx register returns information which is latched in an intermediate buffer. The buffer latches at the middle (i.e. at the falling edge of the clock generator) of the previous clock cycle.
So, reading from the PINx is always delayed for from 0.5 to 1.5 clock cycles.
If the logic level changed in some system clock just before it's middle (i.e. before the falling edge of the clock generator), then this value will be immediately latched, and available for read thru reading the PINx register at the next system clock cycle. Thus, the delay is 0.5 cycles
If the logic level changed just after that latching moment, then, it will be latched only in the next cycle, and will be available for reading in the cycle next after that, thus introducing the delay of 1.5 cycles
The writing to PORTx register updates the output value at the end of the clock cycle, so, it only latched in the next cycle, and will be available for reading only in next cycle after that.
The C compiler is pretty good for optimizaion, so, two consequent lines with PORTA assignment and PINA reading were compiled to just two consequent out PORTA, rxx and in ryy, PINA instructions, which cause that effect
Related
I want to handle case when my MPU6050 is disconnected and reset. Unfortunately, after reinit MPU6050 shows pitch and roll values as 0 and stabilizes after ~1-2s with right values. I would like to hint DMP by writing last read values before reset. Is it any interface for it?
Btw, no matter I configure LPF with value 5 or 188 - 'issue' still exists.
Solution
Try logging the 16 bytes of memory beginning at D_0_192 (defined in inv_mpu_dmp_motion_driver.c of motion_driver_6.12).
unsigned short buf[16];
mpu_read_mem(D_0_192, 16, buf);
// your chosen method of logging this buffer
Mine looked like this shortly after mpu_set_dmp_state(1):
3fffdfeb 003eb3b6 000d2278 00002f3c
and like this after stabilizing for 15 seconds:
1e246556 386e559d 01b407b2 004d6ad9
If my MPU6050-using device starts upside-down, it takes about this long for the readings to stabilize.
After the readings have stabilized, record the value as a constant and write it back to the same location when DMP setup happens:
mpu_write_mem(D_0_192, 16, buf);
Method
I logged the contents of some of the DMP's mentioned but unreferenced/undocumented registers in inv_mpu_dmp_motion_driver.c figuring that they probably expose this part of DMP state somewhere. I did this after a call to mpu_set_dmp_state(1).
I found a number of values that change while the DMP is running. D_0_192, which I believe to be the internal state corresponding to attitude was identifiable by its sluggish relaxation time. I haven't taken the time to interpret the contents. I have only copied the buffer and observed the intended result -- that the contents of the DMP fifo begin at the recorded attitude.
Disclaimer
This is undocumented, potentially very wrong, and certainly brittle (untested on anything other than the DMP firmware blob included in motion_driver_6.12). Use at your own risk.
I'm building a firmware for a device based on Atmel/Microchip AT SAMG55.
In a simple function, trigger some relais connected to GPIO pins.
Because I want to interlock different I/O, avoiding that 2 specific outputs are high level on the same time, I need to know the pin level I set before.
In another project, based on the SAMD21, there was a function that reads output pin state
static inline bool port_pin_get_output_level(const uint8_t gpio_pin)
The SAMG55 port library in ASF is quite different, so i tried ioport_get_pin_level(pin), but i'm not getting expected result. I think that it works only with pins configured as inputs.
Are there any recommended solutions?
Referring to Figure 16-2 in the SAMG55 data sheet, and to sections 16.5.4 and 16.5.8:
16.5.4 Output Control
... The level driven on an I/O line can be determined by writing in the Set Output Data Register (PIO_SODR) and
the Clear Output Data Register (PIO_CODR). These write operations,
respectively, set and clear the Output Data Status Register
(PIO_ODSR), which represents the data driven on the I/O lines. ...
16.5.8 Inputs
The level on each I/O line can be read through PIO_PDSR. This register indicates the level of the I/O lines regardless of their
configuration, whether uniquely as an input, or driven by the PIO
Controller, or driven by a peripheral. Reading the I/O line levels
requires the clock of the PIO Controller to be enabled, otherwise
PIO_PDSR reads the levels present on the I/O line at the time the
clock was disabled.
So, as long as the pin is configured such that the actual level on the pin always corresponds to the level we're trying to drive - which is not the case with an open collector configuration, for example - then Tarick Welling's answer is correct: you can read the output state from the Output Data Status Register (PIO_ODSR).
However the true state of the pin, regardless of driver configuration, can be read (subject to a resynchronisation delay that may or may not be relevant in any given application) from the Pin Data Status Register (PIO_PDSR).
You can do some low level programming. You use the high level HAL functions to configure, set and reset the pins but before you do that you would. Read the value for the pin by addressing the data value of the register. In AVR that would be done by reading PORTx. In a STM32 this can be done by reading the value of GPIOx->ODR. You would of course then need to extract the correct pin but this can be done.
You can also look inside the definition of port_pin_get_output_level and check how they did it and convert that into the way this board/vendor/HAL does its addressing.
update:
When looking inside the datasheet for the SAM G55G/J. Page 340 gives us the answer we need.
The level driven on an I/O line can be determined by writing in the Set Output Data Register (PIO_SODR) and the
Clear Output Data Register (PIO_CODR). These write operations, respectively, set and clear the Output Data
Status Register (PIO_ODSR), which represents the data driven on the I/O lines.
So we can drive the output by writing to PIO_SODR and PIO_CODR to set and reset the pins respectively. But also read from PIO_ODSR this is a register which contains the state of the pin.
A quick google search turns up two options for Atmel/AVR controllers:
read back from the same location you used to set your output value (PORTx register)
This will give you the value that you have written into the register before.
read the actual value using the PINx registers
This will give you the value that you could actually measure on your device.
The difference between the two can be important: if you set a GPIO that is pulled down below the logic voltage threshold (i.e. if connected to GND) to HIGH, PORTx will read HIGH (the value you set) while PINx will read LOW (the actual value).
https://www.avrfreaks.net/forum/reading-pin-set-output
The Arduino A/D converter takes about 0.1ms according to the manual. Actually, my tests show that on an Uno I can execute about 7700 per second in a loop.
Unfortunately analogRead waits while the reading is being performed, making it difficult to get anything done.
I wish to interleave computation with a series of A/D conversions. Is there any way of initiating the analogRead, then checking the timing and getting the completed value later? If this needs to be low-level and non-portable to other versions, I can deal with that.
Looking for a solution that would allow sampling all the channels on an Arduino on a regular basis, then sending data via SPI or I2C. I am willing to consider interrupts, but the sampling must remain extremely periodic.
Yes, you can start an ADC conversion without waiting for it to complete. Instead of using analogRead, check out Nick Gammon's example here, in the "Read Without Blocking" section.
To achieve a regular sample rate, you can either:
1) Let it operate in free-running mode, where it takes samples as fast as it can, or
2) Use a timer ISR to start the ADC, or
3) Use millis() to start a conversion periodically (a common "polling" solution). Be sure to step to the next conversion time by adding to the previously calculated conversion time, not by adding to the current time:
uint32_t last_conversion_time;
void setup()
{
...
last_conversion_time = millis();
}
void loop()
{
if (millis() - last_conversion_time >= ADC_INTERVAL) {
<start a new conversion here>
// Assume we got here as calculated, even if there
// were small delays
last_conversion_time += ADC_INTERVAL; // not millis()+ADC_INTERVAL!
// If there are other delays in your program > ADC_INTERVAL,
// you won't get back in time, and your samples will not
// be regularly-spaced.
Regardless of how you start the conversion periodically, you can either poll for completion or attach an ISR to be called when it is complete.
Be sure to use the volatile keyword for variables which are shared between the ISR and loop.
I am using the readstream interface to sample at 100hz, I have been able to integrate the interface into Oscilloscope application. I just have a doubt in the way I pass on the buffer value on to the packet to be transmitted . Currently this is how I am doing it :
uint8_t i=0;
event void ReadStream.bufferDone( error_t result,uint16_t* buffer, uint16_t count )
{
if (reading < count )
i++;
local.readings[reading++] = buffer[i];
}
I have defined a buffer size of 50, I am not sure this is the way to do it as I am noticing just one sample per packet even though I have set Nreadings=2.
Also the sampling rate does not seem to be 100 samples/second when I check.I am not doing something right in the way I pass data to the packet to be transmitted.
I think I need to clarify a few things according to your questions and comments.
Reading a single sample from an accelerometer on micaZ motes works as follows:
Turn on the accelerometer.
Wait 17 milliseconds. According to the ADXL202E (the accelerometer) datasheet, startup time is 16.3 ms. This is because this particular hardware is capable of providing first reading not immediately after being powered on, but with some delay. If you decrease this delay, you will likely get a wrong reading, however, the behavior is undefined, so you may sometimes get a correct reading or the result may depend on environment conditions, such as ambient temperature. Changing this 17-ms delay to a lower value is certainly a bad idea.
Read values (in two axes) from the Analog to Digital Converter (ADC), which as an MCU component that converts analog output voltage of the accelerometer to the digital value (an integer). The speed at which ADC can sample is independent from the parameters of the accelerometer: it is another piece of hardware.
Turn off the accelerometer.
This is what happens when you call Read.read() in your code. You see that the maximum frequency at which you can sample is once every 17 ms, that is, 58 samples per second. It may be even a bit smaller because of some overhead from MCU or inaccuracy of timers. This is true when you sample by calling Read.read() in a loop or every fixed interval, because this call itself lasts no less than 17 ms (I mean the delay between the command and the event).
What you may want to do is:
Turn on the accelerometer.
Wait 17 ms.
Perform series of reads.
Turn off the accelerometer.
If you do so, you have one 17-ms delay for a set of samples instead of such delay for each sample. What is important, these steps have nothing to do with the interface you use for performing readings. You may call Read.read() multiple times in your application, however, it cannot be the same implementation of the read command that is already implemented for this accelerometer, because the existing implementation is responsible for turning on and off the accelerometer, and it waits 17 ms before reading each sample. For convenience, you may implement the ReadStream interface instead and call it once in your application.
Moreover, you wrote that ReadStream used a microsecond timer and is independent from the 17-ms settling time of the ADC. That sentence is completely wrong. First of all, you cannot say that an interface uses or does not use a timer. The interface is just a set of commands and events without their definitions. A particular implementation of the interface may use timers. The Read and ReadStream interfaces may be implemented multiple times on different platforms by various hardware components, such as accelerometers, thermometers, hygrometers, magnetometers, and so on. Secondly, the 17-ms settling time refers to the accelerometer, not the ADC. And no matter which interface you use, Read or ReadStream, and which timers a driver uses, milli- or microsecond, the 17-ms delay is always required after powering on the accelerometer. As I mentioned, you probably want to make this delay once per multiple reads instead of once per a single read.
It seems that the TinyOS source code already contains an implementation of the accelerometer driver providing the ReadStream interface which allows you to sample continuously. Look at the AccelXStreamC and AccelYStreamC components (in tos/sensorboards/mts300/).
The ReadStream interface consists of two commands. postBuffer(val_t *buf, uint16_t count) is called to provide a buffer for samples. In the accelerometer driver, val_t is defined as uint16_t. You may post multiple buffers, one by one. This command does not yet start sampling and filling buffers. For that purpose, there is a read(uint32_t usPeriod) command, which directs the device to start filling buffers by sampling with the specified period (in microseconds). When a buffer is full, you get an event bufferDone(error_t result, val_t *buf, uint16_t count) and a component starts filling a next buffer, if any. If there are no buffers left, you get additionally an event readDone(error_t result, uint32_t usActualPeriod), which passes to your application a parameter usActualPeriod, which indicates an actual sampling period and may be different (especially, higher) from a period you requested when calling read due to some hardware constraints.
So the solution is to use the ReadStream interface provided by AccelXStreamC and AccelYStreamC (or maybe some higher-level components that use them) and pass an expected period in microseconds to the read command. If the actual period is lower than one you expect, this means that sampling at higher rate is impossible either due to hardware constraints or because it was not implemented in the ADC driver. In the second case, you may try to fix the driver, although it requires good knowledge of low-level programming. The ADC driver source code for this platform is located in tos/chips/atm128/adc.
I have an Arduino Mega connected to a 6 axis robotic arm. All 6 interrupts are attached to encoders (one encoder pin on an interrupt, the other on a vanilla digital input). The interrupts are handled with this code:
void readEncoder1(){
//encoders is a 2d array, where the first d is the axis, and the two pin numbers
//first pin is on an interrupt (CHANGE), and second is a standard digital in
if (digitalRead(encoders[0][0]) == digitalRead(encoders[0][1])) {
positions[0]++;
} else {
positions[0]--;
}
if(servoEnable){
updatePositions(); //// compares positions[] to targets[] and adjusts motor speed accordingly
}
}
This is designed to keep the arm locked at a certain position- if the arduino detects that the position of the motor is off by a certain threshold, it updates the power going to the motor to keep the arm in position.
The problem is this, then -- if two or three (or more) axis are under load (requiring constant updating to stay in position) or they are moving, the Arduino will stop receiving intact commands on Serial input, several characters will be dropped. The interrupts are obviously running quite quickly, and for some reason this is causing commands to become corrupted. Is there any way around this? Architecturally, am I doing this right? My main instinct is to call updatePositions() in the main run loop at, say, 100 ms intervals, will this significantly reduce interrupt overhead? I guess what my question boils down to is how do I get reliable serial commands into the Arduino even if all 6 encoders are pulsing away?
Quadrature encoders were designed to be read by hardware counters. Pulse rates are generally high with the motor running at full speed. One megahertz is not unusual. The higher the number of pulses, the better the servo loop works and the more accurate you can position the motor.
Doing this is in software with a low-power cpu is, well, challenging. It will fall apart when the ISR takes longer than the interval between pulses. You'll lose pulses and thus position. Especially bad because there is no way you can detect this error condition. And that this loss happens when the robot is moving fast, the worst case condition to lose control.
You absolutely cannot afford to update the servo loop in the interrupt handler so get rid of that first. Keep the ISR to the bare minimum, only count the position and nothing else. The servo loop should be separate, driven by a timer interrupt or tick. You cannot properly control a robot with a 100 msec servo update unless it is big an sluggish, this needs to be a handful of milliseconds at most to get smooth acceleration and stable feedback.
There's a limited amount of wisdom in spending forty bucks to control thousands of dollars worth of robot hardware. Not being able to keep up in the servo loop is something you can detect, shut it down when the position error builds up too much. There's nothing you can do about losing pulses, that's a wreck. Get the hardware counters.
First rule of embedded systems:
Do as little as possible in interrupts.
In your case, just update the positions in the interrupt and run your position/speed control loop in the background or at a lower priority.
Aside: I assume you are aware that you are "losing" encoder pulses as you don't have an interrupt on one of the channels?
Also, interrupt-driven encoder-analysis is very noise-prone. If you get a noise pulse, you'll likely only see an interrupt for one of the edges as they'll be too close together to process both.
A more robust way is to use a state machine which watches all 4 transitions, but that requires either interrupts on both edges of both channels, or polling fast enough to not miss anything up the to rate you are expecting to see.