Recently I am implementing the internal peripherals of STM32F0 family mcu in a c library.
While I was looking at how ST managed to do the same with their HAL library I stepped into this piece of code...
1 /*--------------------- EXTI Mode Configuration ------------------------*/
2 /* Configure the External Interrupt or event for the current IO */
3 if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
4 {
5 /* Enable SYSCFG Clock */
6 __HAL_RCC_SYSCFG_CLK_ENABLE();
7 temp = SYSCFG->EXTICR[position >> 2];
8 CLEAR_BIT(temp, (0x0FU) << (4U * (position & 0x03U)));
9 SET_BIT(temp, (GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
10 SYSCFG->EXTICR[position >> 2] = temp;
11 /* Clear EXTI line configuration */
12 temp = EXTI->IMR;
13 CLEAR_BIT(temp, (uint32_t)iocurrent);
14 if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
15 {
16 SET_BIT(temp, iocurrent);
17 }
18 EXTI->IMR = temp;
What I am trying to understand is why the pair commands in lines 7-10 and 12-18 are used. Why are there? Why store something then write it and then retrieve it from memory discarted wroten content?
Is it about multitasking and race conditions? I do not fully understand!
This is used for speed optimization and to avoid unwanted states in memory-mapped registers.
Imagine some example memory-mapped variable EXT->ABC. Imagine such exaggerated example situation:
We have some hardware memory address that is accessible via memory-mapped EXT->ABC variable
We know that bit 1 is set and bit 2 is cleared in EXT->ABC
We want to clear bit 1 and set bit 2 and leave all other bits unchanged in EXT->ABC
Hardware doesn't allow bit 1 and bit 2 in EXT->ABC register to be both cleared or both set. Such states are forbidden and may result in undefined behavior (that means in ANY behavior, for example software reset).
Reading and writing to EXT->ABC register is very very slow.
If we operate on EXT->ABC variable directly:
CLEAR_BIT(EXT->ABC, 1); // expands to: EXT->ABC = EXT->ABC & ~1;
SET_BIT(EXT->ABC, 2); // expands to: EXT->ABC = EXT->ABC | 2;
this will result in a forbidden state in between the two lines, as bit 1 and bit 2 will be set. Also, in these 2 lines, EXTI->IMR is accessed 4 times.
To resolve this, we need to store the value in EXTI->IMR register somewhere, modify needed bits, and then write the modified value to EXTI->IMR register. So we will:
uint32_t temp;
temp = EXT->ABC; // store
CLEAR_BIT(temp, 1);
SET_BIT(temp, 2);
EXT->ABC = temp; // write
We read once from a memory-mapped variable, modify&play with the value, do whatever we want. After that, we write once to memory-mapped variable. That way EXT->ABC register is touched only twice and code is written safely, so that no undefined/forbidden/unwanted states occur.
For example: as for lines 7-10: to access SYSCFG->EXTICR[position >> 2U] you need multiple cpu operations (calculate position >> 2U, multiple by sizeof(SYSCFG->EXTICR), add to SYSCFG->EXTICR address, dereference). Maybe it was developer intention to execute these lines faster. Or you need to clear bits 0x0F<<(4*(position&0x03) when writting to (GPIO_GET_INDEX(GPIOx))<<(4*position&0x03).
It is to make the operations "atomic". All changes to the registers in one write operation.
I can't see anything strange:
Lines 7-10 and 12-18 copy what seems to be the contents of a register to a local variable, do some operation on it and then store the result back to the original register.
The reason why this may happen are multiple, but my guess is that the author of the code did not want the MCU to be in an intermediate state while tinkering with the registers.
For example, what happens to the core after line 8 but before line 9?
Related
I am currently programming a Teensy micro-controller and want to setup a pause function for a game. I have been able to create a timer from ISR counter overflows, however I haven't been able to work out how to pause this counter. I have tried:
ISR(TIMER1_OVF_vect) {
if (paused == 0){
overflow_counter ++;
}
}
This seems to make no difference to the counter, the counter just keeps going no matter what instructions I put inside the function. I have tried a variety of if statements and they are ignored - the function just increases the counter for some reason (even if I put overflow_counter --)!
So I have tried to setup another variable that takes a snapshot of the time when the pause button is pressed, then when the game is un-paused it would take another snapshot and calculate the difference. This would then be taken away from the overall time.
double snapshot_time = 0;
double get_current_time(void){
double time = ( overflow_counter * 65536.0 + TCNT1 ) * PRESCALE / FREQ;
if (paused == 0 && switch_state[5] == 1){
snapshot_time = time;
}
return time;
}
I have tried setting snapshot_time as a global variable and making it equal to time thinking this may dynamically capture a static variable but unfortunately it doesn't. Can anyone offer a way to do this?
There are many aspects hidden in your question.
1. First of all, the counter variable should be marked volatile. The compiler is applying different optimizations to variables, so it may, say, load a variable into a register and continue to work with register, assuming it is only the place, where content of the variable is stored. If a variable is declared with the keyword volatile, then the compiler knows that it could be changed occasionally in any time, therefore the compiler will reload and/or rewrite the variable each time it is accessed. So, it may be declared like this
volatile uint16_t overflow_counter;
The same goes for the paused variable.
2. You should remember that, if interrupts are not disabled, then timer interrupt can occur between any two processor's instructions. Since processor is 8bit, it access the memory using 8-bit wide bus. That means, to read 16-bit data, it requires 2 instructions. Let's say we copy the counter value into a local variable:
uint16_t counter_snapshot = overflow_counter;
The local variable will allocate two registers and two memory read operations will be performed. Let's imagine the interrupt is happened after first of them, but before second. So, on output you'll have half of the number copied from it's previous value, while the second half from it's new. I.e. the value will be damaged. It will not be happened, if variable is 8-bit and copied by one instruction. But if it is wider, or if it is read-modified-written, then precautions should be taken:
uint8_t old_sreg = SREG; // SREG i/o register contains processor state flags, including "interrupt flag", which allows interrupt
cli(); // clear the "interrupt flag", preventing interrupts from happening
uint16_t counter_snapshot = overflow_counter; // safely taking the snapshot
SREG = old_sreg; // restoring the "interrupt flag" to it's previous state. Other flags also restored but we do not care about them.
3. As said above, interrupt can happen in any time. That means if you try to read overflow_counter and TCNT1 both, the interrupt can be happened in between, so, result will be not as expected. Especially if reading of those two values is separated by such a long operation as floating-point multiplication. So, workaround may be as follows:
uint8_t old_sreg = SREG; // saving state
cli(); // locking interrupts
// latching values we are interested in
uint16_t latch_overflow_counter = overflow_counter;
uint16_t latch_tcnt1 = TCNT1;
uint8_t latch_tifr1 = TIFR1;
SREG = old_sreg; // restoring interrupts
/* We are disabling interrupts, but it do not stop the timer from counting,
therefore TCNT1 value continue changing, and timer could overflow in any time
within that block above. But which moment exactly? Before we read TCNT1 or just after?
Let's assume if TCNT1 have high values then we got it's value just before the timer overflow;
otherwise, overflow happened before that */
if ((latch_tifr1 & (1 << TOV1)) && // we got the overflow flag set
(latch_tcnt < 32768) { // we are within the low values
latch_overflow_counter++; // increasing the latched value
}
double time = ( latch_overflow_counter * 65536.0 + latch_tcnt1 ) * PRESCALE / FREQ; // now using latched values to calculate...
By the way, throughput can be much improved, if avoid using floating point where it is not necessary.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am trying to read ADC(ADS8320) values using SPI communication. I am using IAR embedded workbench for ARM and SM470R1B1 controller. In the data sheet it says first 6 bits are dummy read and next 16 bits is the actval data. I am trying to read 24 bits and ignore first 6 bits and last 2 bits. I am getting wrong values when i try with the below code.
unsigned int readADC8320(void)
{
value = AD8320_16(0xFFFFFF); // read registe
return value;
}
unsigned int AD8320_16(unsigned int value)
{
unsigned int data;
AD8320_CS_Status(0);
SW_Delay(DELAY_10US);
while(GIODOUTA&X2);
data = spi2(value >> 16); //read high 8 bits
data = (data << 6)| spi2(value >> 8); //read next 8 bits but 6+8 =14
data = (data << 8)| spi2(value >> 2); //add last two bits only
SW_Delay(DELAY_10US);
AD8320_CS_Status(1);
return data;
}
unsigned char spi2(unsigned char data)
{
// Write byte to SPI2DAT1 register
SPI2DAT1 = data;
while(!(SPI2CTRL3 & 0x01)){} // Wait for RxFlag to get set
return (SPI2BUF & 0x000000FF); // Read SPIBUF
}
Can anyone suggest me where i am doing wrong. I am quite poor with shift operations.
Your code looks wonky. For example, what purpose does value have?
The datasheet you linked to indicates that the 22 clock cycles suffice (and that 24 clock cycles are okay), no extra delays are needed, assuming the SPI clock is within limits (at most 2.4 MHz, I believe). So, I'd expect your code to look more like
void AD8320_setup(void)
{
/* TODO:
* - Set SPI2 clock frequency, if programmable (max. 2.4 MHz)
* - Set SPI2 transfer size, if programmable (8 bits per byte)
* - Set SPI2 to latch data on the falling edge of clock pulse
* - Set SPI2 to pull clock high when inactive
* - Set AD8320 chip select pin as an output
* - Set AD8320 chip select pin high (it is active low)
* - Ensure AD8320 is powered
*/
}
uint16_t AD8320_16(void)
{
uint16_t result;
/* TODO: Set AD8320 chip select pin 0/LOW (to select it)
*/
/* TODO: Clear SPI2 FIFO if it has one,
* so that we get actual wire data,
* not old cached data.
*/
result = ((uint16_t)(spi2_read() & 3U)) << 14;
result |= ((uint16_t)spi2_read()) << 6;
result |= ((uint16_t)spi2_read()) >> 2;
/* TODO: Set AD8320 chip select pin 1/HIGH (to deselect it)
*/
return result;
}
The prototype for spi2_read is typically unsigned char spi2_read(void);, but it might be of any integer type that can represent all eight bit values correctly (0 to 255, inclusive). Above, I cast the result -- after masking out any irrelevant bits, if any, with a binary AND operation -- to uint16_t before shifting it into the correct position in the result, so that regardless of the spi2_read() return type, we can use it to construct our 16-bit value.
The first spi2_read() reads the first 8 bits. I am assuming your device SPI bus sends and receives the most significant bit first; this means that the 6 dummy bits are most significant bits, and the 2 least significant bits are the most significant bits of the result. That's why we only keep the two least significant bits of the first byte, and transfer it 14 places left -- that way we get them in the most significant position.
The second spi2_read() reads the next eight bits. These are bits 13..6 of the data; that's why we shift this byte left by 6 places, and OR with the existing data.
The last spi2_read() reads the next eight bits. The last two bits are to be discarded, because only the first (most significant) 6 bits contain the rest of the data we're interested in. So, we shift this down by 2 places, and OR with the existing data.
Preparing spi2 involves stuff like word size (8 bits), data rate (if programmable), and so on.
The chip select for the AD8320 is just a generic output pin, it's just active low. That's why it's described as ^CS/SHDN in the datasheet: chip selected when low, shutdown when high.
The chip is a micropower one, so it requires less than 2mA, so you could conceivably power it from an output pin on many microcontrollers. If you were to do so, you'd also have to remember to make that pin an output and 1/high in _setup() function.
I am trying to read the data from FXLS8471Q 3-Axis, Linear Accelerometer using SPI. I am using bit banging method to read the data from Accelerometer. I am using LPC 2184 ARM processor. I used the following code.
unsigned char spiReadReg (const unsigned char regAddr)
{
unsigned char SPICount;
unsigned char SPIData;
SPI_CS = 1;
SPI_CK = 0;
SPIData = regAddr;
SPI_CS = 0;
for (SPICount = 0; SPICount < 8; SPICount++)
{
if (SPIData & 0x80)
SPI_MOSI = 1;
else
SPI_MOSI = 0;
SPI_CK = 1;
SPI_CK = 0;
SPIData <<= 1;
}
SPI_MOSI = 0;
SPIData = 0;
for (SPICount = 0; SPICount < 8; SPICount++)
{
SPIData <<=1;
SPI_CK = 1;
SPIData += SPI_MISO;
SPI_CK = 0;
SPIData &=(0xFE);
}
SPI_CS = 1;
return ((unsigned char)SPIData);
}
But instead of getting valid value 0x6A , I am getting garbage value.
Please help me out to solve this problem;
SPIData &=(0xFE); as pointed out in another answer, is definitely wrong as it erases the bit you just received.
However, there are other major issues with your code.
An SPI slave device sends you data by setting the value of MISO on a rising or falling clock, depending on the type of device. However, you didn't wait in your code for the value to appear on MISO.
You control the communication by setting the clock to 1 and 0. The datasheet of the accelerometer says on page 19, that
Data is sampled during the rising edge of SCLK and set up during the falling edge of SCLK.
This means that in order to read from it, your processor needs to change the clock from one to zero, thereby signaling the accelerometer to send the next bit to the MISO. This means you did the reverse, you in your code read on a rising edge while you should be reading on the falling edge. After setting the clock to zero, you have to wait a little while until the value appears on MISO, and only then should you read it and add it to your SPIData variable. Table 9, SPI timing indicates how much you have to wait: at least 500 nanoseconds. That's not much, but if your CPU runs faster than 2 MHz then if you don't use a delay, you will try to read the MISO before the accelerometer had time to properly set it.
You also forgot the slave Select, which is actually required to indicate the beginning and the end of a datagram.
Check the Figure 7. SPI Timing Diagram in the datasheet, it indicates what you are required to do and in what order, to communicate with the device using SPI.
I also suggest reading about how the rotating registers of the SPI work, because it seems from its datasheet, that the accelerometer needs to receive a well specified number of bits before useful data appears on its output. Don't forget, as you send a single bit to the device, it also has to send a bit back to you, so if it didn't decode a command yet, it can only send gibberish. Your code, as the master, can only "push" bits in, and collect the bits which "pop out" on the other side. This means you have to send a command, and then send further bits until all the answer is pushed out to you bit by bit.
If you get stuck, I think you will have much more luck getting help on https://electronics.stackexchange.com/, but instead of just putting this same code there (which seems to be blindly copied from www.maximintegrated.com and has absolutely nothing to do with the problem you try to solve), I strongly recommend trying to understand the "Figure 7. SPI Timing Diagram" I was suggesting before, and alter your code accordingly.
Without understanding how the device you try to communicate with works, you will never succeed if you just blindly copy the code from a completely different project just because it says "spi" in the title.
SPIData &=(0xFE);
The above line is causing the problem. Here the LSB is reset to 0 (which contained the data bit just taken from MISO) -- basically you are destroying the bit you just read. Omitting the line should correct the problem.
be sure to compile your function with NO optimization
as that will corrupt the bitbang operation.
this is the code from the maxum site for the spiReadReg function.
(which looks like were you got your code.
However, this is just a guide for the 'general' sequence of operations for communicating with the maxim 1481 part.
the accel. part needs several setup commands and reads completely differently
Suggest reading the app notes and white papers available at freescale.com for the specific part number.
Those app notes/white papers will indicate the sequence of commands needed for setting up a specific mode of operation and how to request/interpret the resulting data.
There are a number of device specifics that your code has not taken into account.
Per the spec sheet the first bit transmitted is a read/write indicator, followed by 8 bits of register address, followed by 7 trash bits (suggest sending all 0's for the trash bits.) followed by the data bits.
Depending on the setup commands, those data bits could be 8 bits or 14 bits or multiple registers of 8 or 14 bits per register.
I'm writing a program for an ATMega328P that will take readings from several ADC channels, combine them into a single signal and output this signal through PWM.
I've successfully backed off my ADC polling to 50Hz per channel using Single Conversion mode. I'm using Timer/Counter2 for PWM generation, and Timer/Counter1 for doing the calculations I need to do to set compare values for Timer/Counter2. This is the ISR for Timer/Counter1:
// Interrupt service routine called to generate PWM compare values
ISR(TIMER1_COMPA_vect)
{
// Grab most recent ADC reading for ADC0
uint32_t sensor_value_0 = adc_readings[0];
// Get current value for base waveform from wavetable stored in sinewave_data
uint32_t sample_value_0 = pgm_read_byte(&sinewave_data[sample_0]);
// Multiply these two values together
// In other words, use the ADC reading to modulate the amplitude of base wave
uint32_t sine_0 = (sample_value_0 * sensor_value_0) >> 10;
// Do the same thing for ADC2
uint32_t sensor_value_1 = adc_readings[1];
uint32_t sample_value_1 = pgm_read_byte(&sinewave_data[sample_1]);
uint32_t sine_1 = (sample_value_1 * sensor_value_1) >> 10;
// Add channels together, divide by two, set compare register for PWM
OCR2A = (sine_0 + sine_1) >> 1;
// Move successive ADC base waves through wavetable at integral increments
// i.e., ADC0 is carried by a 200Hz sine wave, ADC1 at 300Hz, etc.
sample_0 += 2;
sample_1 += 3;
// Wrap back to front of wavetable, if necessary
if (sample_0 >= sinewave_length) {
sample_0 = 0;
}
if (sample_1 >= sinewave_length) {
sample_1 = 0;
}
} // END - Interrupt service routine called to generate PWM compare values
My problem is that that I get no PWM output. If I set either sensor_value_0 or sensor_value_1 to 1024 and leave the other sensor_value_ set to read from the ADC, I do get one full-amplitude component wave, and an amplitude-modulated component wave. If however, I choose a different value for the hardcoded, mock amplitude, I am not so lucky (1023, for instance). Any other values give me no PWM output. If I set both sensor_value_s to look at the same ADC channel, I would expect two component waves whose amplitudes are modulated identically. Instead, I get no PWM output. What is most confusing of all to me is that if I choose a value for the hardcoded amplitude that is an exact power of two, all is well.
The whole power-of-two part makes this seem to me to be a bit-twiddling issue that I'm not seeing. Can you see what I must have clearly missed? I'd appreciate any tips at all!
(I've posted my entire source here to keep things as neat as possible on SO.)
Your issue may be caused by the architecture of the AVR that you're developing on. The ATMega328p has 8 bit registers, similar to most other AVR chips. This means that the 32b values that you're working with must be stored in memory by the compiler and broken up into four separate registers every time you perform arithmetic on them. In fact, there are no arithmetic instructions that perform on more than one register at once, so I'm really not sure what the compiler is doing!
I'd be interested to know what the disassembly of your code is, but my guess is that gcc is using the MUL instruction to execute the sample_value_0 * sensor_value_0 code. This instruction operates on two 8b values and produces a 16b value, so I wouldn't be surprised if the reason you're seeing an odd dependence on multiples of two produce results.
I'd say try reworking this block of code by changing the data types of the variables. Use uint8_t for sensor_value_* and sample_value_*, and uint16_t for sine_*. Then, to make sure everything fits in the 8b OCR2A register, change the assignment to something like:
OCR2A = (sine_0 + sine_1) & 0xFF;
#Devrin, I appreciate the response, but just manipulating types didn't do it for me. Here's what I ended up doing:
uint8_t sine_0 = (pgm_read_byte(&sinewave_data[sample_0]) >> 5) * (adc_readings[1] >> 5);
uint8_t sine_1 = (pgm_read_byte(&sinewave_data[sample_1]) >> 5) * (adc_readings[2] >> 5);
OCR2A = (sine_0 >> 1) + (sine_1 >> 1);
Essentially, I've done all my shifting immediately, instead of waiting until the last minute. Unfortunately, I lose a lot of precision, but at least the code works as expected. Now, I wil begin cranking things back up to find the initial cause of my issues.
I am using an IC, DS1620 to read 1 bit serial data coming on a single line. I need to read this data using one of the ports of ARM microcontroller (LPC2378). ARM ports are 32 bit. How do I get this value into a 1 bit variable?
Edit: In other words I need direct reference to a port pin.
there are no 1 bit variables, but you could isolate a particular bit for example:
uint32_t original_value = whatever();
uint32_t bit15 = (original_value >> 15) & 1; /*bit15 now contains either a 1 or a 0 representing the 15th bit */
Note: I don't know if you were counting bit numbers starting at 0 or 1, so the >> 15 may be off by one, but you get the idea.
The other option is to use bit fields, but that gets messy and IMO is not worth it unless every bit in the value is useful in some way. If you just want one or two bits, shifting and masking is the way to go.
Overall, this article may be of use to you.
For your CPU the answer from Evan Teran should be used. I just wanted to mention the bit-band feature of some other ARM CPU's like the Cortex-M3. For some region of RAM/Peripherals all bits are mapped to a separate address for easy access.
See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337e/Behcjiic.html for more information.
It's simple if you can access the port register directly (I don't have any experience with ARM), just bitwise AND it with the binary mask that corresponds to the bit you want:
var = (PORT_REGISTER & 0x00008000);
Now var contains either 0 if the 15th bit is '0' or 0x00008000 if the 15th bit is '1'.
Also, you can shift it if you want to have either '0' or '1':
var = ((PORT_REGISTER & 0x00008000) >> 15);
The header file(s) which come with your compiler will contains declarations for all of the microcontroller's registers, and the bits in those registers.
In this specific article, let's pretend that the port input register is called PORTA, and the bit you want has a mask defined for it called PORTA15.
Then to read the state of that pin:
PinIsSet = (PORTA & PORTA15) == PORTA15;
Or equivalently, using the ternary operator:
PinIsSet = (PORTA & PORTA15) ? 1 : 0;
As a general point, refer to the reference manual for what all the registers and bits do. Also, look at some examples. (This page on the Keil website contains both, and there are plenty of other examples on the web.)
In LPC2378 ( as the other LPC2xxxx microcontroller family ), I/O ports are in system memory, so you need to declare some variables like this:
#define DALLAS_PIN (*(volatile unsigned long int *)(0xE0028000)) /* Port 0 data register */
#define DALLAS_DDR (*(volatile unsigned long int *)(0xE0028008)) /* Port 0 data direction reg */
#define DALLAS_PIN (1<<15)
Please note that 0xE0028000 is the address for the data register of port0, and 0xE0028008 is the data direction register address for port0. You need to modify this according to the port and bit used in your app.
After that, in your code function, the code or macros for write 1, write 0 and read must be something like this:
#define set_dqout() (DALLAS_DDR&=~DALLAS_PIN) /* Let the pull-up force one, putting I/O pin in input mode */
#define reset_dqout() (DALLAS_DDR|=DALLAS_PIN,DALLAS_PORT&=~DALLAS_PIN) /* force zero putting the I/O in output mode and writing zero on it */
#define read_dqin() (DALLAS_DDR&=~DALLAS_PIN,((DALLAS_PORT & DALLAS_PIN)!= 0)) /* put i/o in input mode and test the state of the i/o pin */
I hope this can help.
Regards!
If testing for bits, it is good to keep in mind the operator evaluation order of C, e.g.
if( port & 0x80 && whatever() )
can result in unexpected behaviour, as you just wrote
if( port & (0x80 && whatever()) )
but probably ment
if( (port & 0x80) && whatever() )