My goal is to have a timer that triggers an interrupt at 1kHz and for that to call a few lines of code that starts a HAL_SPI_TransmitReceive_DMA call (or possibly more than one on different SPI buses). Once the SPI calls are made, it enters a while loop waiting for the SPI communication to finish, then it interprets the data received.
I've set up my timer (TIM3) and SPI (SPI1) peripheral using STM32CubeMX. The SPI bus and its DMA channel interrupts have NVIC priorities of 0 while the timer interrupt priority is set to 10. I added this code to the TIM3_IRQHandler function created in the STM32f4xx_it.c file by CubeMX.
'''
void TIM3_IRQHandler(void) {
/* USER CODE BEGIN TIM3_IRQn 0 */
imu_Tx[0] = FirstData_Reg | SPIReadMask;
while (completedSPI1 != 1);
//imu0 start
HAL_GPIO_WritePin(ICM0.CS_Port, ICM0.CS_Pin, GPIO_PIN_RESET);
completedSPI1 = 0;
HAL_SPI_TransmitReceive_DMA(&hspi1, imu_Tx, imu0_reading, 15);
//imu0 end
while (completedSPI1 != 1);
imu_reading_to_data(IMU0_data, imu0_reading);
imu_int_to_norm_float(IMU0_floats, IMU0_data, &ICM0);
/* USER CODE END TIM3_IRQn 0 */
HAL_TIM_IRQHandler(&htim3);
/* USER CODE BEGIN TIM3_IRQn 1 */
/* USER CODE END TIM3_IRQn 1 */
}
'''
I also added this function at the end of STM32f4xx_it.c:
'''
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
if (hspi == &hspi1){
HAL_GPIO_WritePin(ICM0.CS_Port, ICM0.CS_Pin, GPIO_PIN_SET);
completedSPI1 = 1;
}
}
'''
"completedSPI1" is declared as a "volatile int" at the top of the file.
My problem is that the code never makes it to the SPI callback function because it gets locked up in the "while (completedSPI1 != 1);" loop (the second one, under "//imu0 end"). If I comment out that line, it does make it to the callback function (but of course that ruins the function of the code since I'm interpreting data that I haven't finished reading yet). I feel like I must have something configured incorrectly such that the timer interrupt is higher priority than the SPI interrupt even though I was sure to not do that in CubeMX.
The other thing that has got me extra confused is that this code is pretty much copy and pasted from one of my previous projects that reads data from the same sensor in the same way and that worked just fine. Though that project was based on an STM32F7, not an F4.
Please let me know if you've got any advice on how to fix this problem or ideas of other ways I could structure my code to avoid it in the first place while retaining the intended functionality. And of course let me know if there is some important information that I failed to share.
Thanks a lot!
-Ben
Related
I'm trying to get the external interrupt running on a
Nucleo-F030R8
and hit a wall.
Everything is configured and runs just fine in step mode but when I'm connecting my board to another testboard with a simple jumper wire and run the same code, an External Interrupt is triggered even when that testboard (a second
Nucleo-F302R8,
which should only produce a single signal peak that I want to measure with the first) is not turned on.
I'm using a mix of the HAL Library from STM and a bit code of my own.
Has somebody eventually encountered a similar problem?
I'm using the System Workbench for STM32.
Part of the ISR, Interrupthandler is cut
void EXTI0_1_IRQHandler(void)
{
/* USER CODE BEGIN EXTI0_1_IRQn 0 */
if ((EXTI->IMR & EXTI_IMR_MR0) && (EXTI->PR & EXTI_PR_PR0))
{
int_flag_pin.copen = 1;
}
if ((EXTI->IMR & EXTI_IMR_MR1) && (EXTI->PR & EXTI_PR_PR1))
{
int_flag_pin.ma1 = 1;
}
/* USER CODE END EXTI0_1_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
/* USER CODE BEGIN EXTI0_1_IRQn 1 */
/* USER CODE END EXTI0_1_IRQn 1 */
}
Between setting the Pin low and change rising, the Interrupt triggers.
TEST_GPIO_Port->BSRR = (uint32_t) TEST_Pin;
//HAL_GPIO_WritePin(TEST_GPIO_Port, TEST_Pin, GPIO_PIN_RESET);
TEST_GPIO_Port->BRR = (uint32_t) TEST_Pin;
change_rising(0);
Update:
Could it be that resetting the Pin through BSRR or BRR generates an interrupt?
I'm checking my code step-by-step and everytime the pin is getting resetted the interrupt is generated.
if TEST_Pin is GPIO_PIN_0 or GPIO_PIN_1 pin, you will receive irq legally. EXTI0_1_IRQHandler catches irq from any port but from #0 or #1 pin.
I'm not sure if I'm missing something obvious here but has anyone else experienced any issues with the Cortex M0+ (or any of the Cortex M range) returning from an ISR to a somewhat random position in thread mode after being woken up from sleep mode (eg not returning to the line below the WFI instruction). It seems to wake up in the middle of the code, like in the middle of a for loop or the middle of a function. I'm using the ATSAMD218A microcontroller and have been using the debugger in Atmel Studio 7. I've attempted to isolate the problem and have noticed the following:
It only occurs when the device is put to sleep, when the device is interrupted while it is awake the ISR always returns to the right place.
The ISR always returns to the same (incorrect) position in the code, if I comment out that function or piece of code then it begins returning to another incorrect position
I've unsuccessfully inserted delays and NOPs to try determine if it is timing related or clock cycle related but it still always returns to the same (incorrect) position
I have tried implementing interrupts using both direct register access as well as using the Arduino Interrupts library.
It occurs in both idle and standby mode (sleep and deep sleep)
While looking at the assembly instructions during debugging, right before the ISR is exited, the 'bx lr' instruction is called which is supposed to branch to the link register. The link register contains a value called EXC_RETURN which indicates the return behavior, which in my case is 0xFFFFFFF9 (return to thread mode). I can't however find the actual memory address that it returns to anywhere. The memory address isn't in any of the core registers R0-R15.
I have been reading the ARMv6 Architecture Reference Manual as well as the Cortex M0+ Generic User Guide. Not quite sure what's going on and any debugging suggestions would be much appreciated. Does anyone have a better understanding of the exception handling of the Cortex M series and could point me in the right direction to find the memory address that thread mode returns to after an ISR. I could supply code if you'd like but even a simple piece of code that does nothing but count in a loop, sleep and then wake up causes trouble.
EDIT
I've added the relevant code below. It is the most stripped back version (*stripped) that still causes problems. I haven't included the RTC code functions (uses the DS3231RTC library) as I'm fairly certain they don't have any effect. If you think I should upload more let me know.
void configInterrupt(void){
NVIC_DisableIRQ(EIC_IRQn);
NVIC_ClearPendingIRQ(EIC_IRQn);
NVIC_SetPriority(EIC_IRQn, 0);
NVIC_EnableIRQ(EIC_IRQn);
// Enable GCLK for IEC (External Interrupt Controller)
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_EIC));
EIC->WAKEUP.reg |= (1 << 0);
EIC->CONFIG[0].reg |= 0x2; // falling edge
pinConfig(16,INPUT,UP); // custom 'pinMode' function
PORT->Group[0].PINCFG[16].bit.PMUXEN = 1; // enable peripheral muxing
PORT->Group[0].PMUX[8].bit.PMUXE = 0x0; // function A (EIC) = 0x0
EIC->INTENSET.reg = EIC_INTENSET_EXTINT(1 << 0);
EIC->CTRL.bit.ENABLE = 1;
}
void EIC_Handler(void){
RTC_FLAG = 1; // my debug breakpoint is here, at this point the stack has already been pushed and I can see the PC value that will be popped off
int_count++;
EIC->INTFLAG.reg = 1 << 0;
}
void setup() {
configInterrupt();
configureRTC();
RTC_FLAG = 1;
}
void loop() {
if (RTC_FLAG == 1) {
RTC_FLAG = 0;
setNextAlarm();
}
for (int i = 0 ; i <= WINDOW-1 ; i++) {
String data = "";
rawVal = 0; // data gets read from sensor here (stripped)
data += String(rawVal);
data += ",";
distance = 0; // distance calculated from rawVal here (stripped)
data += String(distance);
data += ",";
mean = 0; // mean calculated in a function here (stripped)
data += String(mean);
data += ",";
data += String(int_count); // ISR returns here
// Data gets written to file here (stripped)
}
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__DSB();
__WFI();
}
I'd try to implement the same snippet using Atmel ASF framework, look at the registers the API sets and what order. They will also usually reference errata and implement a work around.
Load the low power example project, it switches between power modes, and wakes on the xplained board button press. If that can hop between modes on your board, you know it's not the part. You might have to move the wake pin to something to match your hardware.
I am working on a project in C on the raspberry pi 2 in which the pi is polling a microcontroller via SPI when the microcontroller asserts a particular pin.
There are two functions that are intended to be executed in this fashion. One of the functions - LNK_pollNetwork() - checks to see if the request pin is high and - if it is - then it downloads the data until the pin is low again, then parses. The other function - LNK_generateNetStat() - sends a byte requesting the network status and then downloads data until the pin is low again.
It seems that if I place a 'printf' statement just after the poll byte in LNK_generateNetStat(), everything works fine. If I remove the printf statement, then the program goes haywire and it is clear to me that the other function is executing almost in parallel with this function.
Both functions are on the same thread... or so I believe.
void LNK_generateNetStat(){
uint8_t statusSerialString[(NETSTAT_HEADER_BYTES
+ (MAX_NUM_OF_NODES
* NETSTAT_FIELD_WIDTH_BYTES))];
uint8_t i = 0;
SPI_transfer(0x86);
/* if this printf is executed, all works normally */
if(verbose)
printf("Network status polled");
while(bcm2835_gpio_lev(REQ_PIN) == HIGH){
statusSerialString[i] = SPI_transfer(0xfe);
i++;
}
/* parsing code below this line */
/* ... */
}
The other function simply polls the request pin and, if it is high, then starts pulling data until the pin is low:
void LNK_pollNetwork(){
if(bcm2835_gpio_lev(REQ_PIN) == HIGH){
int i = 0;
uint8_t payload[PAYLOAD_MAX_LENGTH];
SPI_transfer(0xff); // dummy read - allows slave to load the buffer
while(bcm2835_gpio_lev(REQ_PIN) == HIGH){
payload[i] = SPI_transfer(0xff);
i++;
}
/* payload parsing below this line */
/* ... */
}
}
Both of these should be executed sequentially. There is a higher level task manager that executes LNK_pollNetwork() every 1ms and LNK_generateNetStat() every 1250ms.
I know that LNK_generateNetStat() is being pre-empted because I have used different values in the SPI polling routine for each function in order to identify what is going on. The 0x86 executes normally and should begin polling with bytes numbered 0xfe, but I am seeing 0xff bytes in many cases and - sometimes - they are intermixed. I'm using a logic analyzer to observe.
Thoughts?
Trying to read the PS1 values. But as im running the following code it keeps saying on "chip_stat" that its suspended.
main (void){
init(); // Configuration initialization
si1141_init(); // Si1141 sensor initialization
__delay_ms(30); // Delay to ensure Si1141 is completely booted, must be atleast 25ms
si1141_WriteToRegister(REG_IRQ_STATUS, 0xFF); // Clear interrupt source
signed int status;
while(1){
WriteToI2C(0x5A<<1); // Slave address
PutByteI2C(0x30); // chip_stat
ReadFromI2C(0x5A<<1); // Slave address
if((status = GetByteI2C(0x30)) == Sw_I2C_ERROR) // chip_stat
{
return Sw_I2C_ERROR;
}
Stop_I2C();
status++;;
}
}
The code im using to read the PS1 values is the following. Im reading the value 16705. Which keeps being the same on all measurements.
The value should go up and down from 0 - 32767, as it measures more or less movement.
signed int si1141_ReadFromRegister(unsigned char reg){
signed int data;
WriteToI2C(0x5A<<1); // Slave address
ReadFromI2C(0x5A<<1); // Slave address
if((data = GetByteI2C(Sw_I2C_LAST)) == Sw_I2C_ERROR)
{
return Sw_I2C_ERROR;
}
Stop_I2C();
return data;
}
main (void){
init(); // Configuration initialization
si1141_init(); // Si1141 sensor initialization
__delay_ms(30); // Delay to ensure Si1141 is completely booted, must be atleast 25ms
si1141_WriteToRegister(REG_IRQ_STATUS, 0xFF); // Clear interrupt source
signed int PS1;
while(1){
PS1 = si1141_ReadFromRegister(REG_PS1_DATA0) + (256 * si1141_ReadFromRegister(REG_PS1_DATA1)); // Proximity CH1
}
}
I linked the files for the i2c communication.
https://www.dropbox.com/s/q41vw444gjvj0qa/swi2c.c?dl=0
https://www.dropbox.com/s/1mshyz88o15hz8c/swi2c.h?dl=0
Rule out I2C errors first. Your software I2C library is no help at all.
Make sure you read registers PART_ID, REV_ID, SEQ_ED first and that the values match the data sheet resp. your expected values. This is to rule out I2C errors.
You have to take quite a few steps to get a single reading to get started.
Reset the Si114x. Program the HW_KEY. Program PS_LED21 to a sensible value. The ANs tell you how. Do not program a higher value than what your components can handle and what your design can support. This might destroy something if done incorrectly. Do not get any funny ideas about PS_ADC_GAIN either, or you will fry your device. Read the AN. Do not program PS_ADC_GAIN at this point.
Clear PSLED21_SELECT -- only PS2_LED, keep PS1_LED set for LED1, obviously -- and PSLED3_SELECT. This is probably optional, but the datasheet tells you to do it, so do it.
Next, program CH_LIST to PS1_EN, then send a PS_FORCE command.** Now read PS1 data from PS1_DATA0 and PS1_DATA1. Done.
It may be easier to test with ALS first to rule out saturating your sensor with some stray infrared (think setting sun as you work through the night).
** For the command protocol, you have to implement the command/response protocol laid out in the datasheet. I suggest you test with reset and nop first to verify your code.
I'm facing a weird issue. I've always used bit bangin I2C functions on my PIC16F1459, but now I want to use the MSSP (SPI,I2C Master Slave Peripheral). So I've started writing the functions according to the datasheet, Start, Stop, etc. The problem I have is my PIC won't ACK the data I send to the I2C EEPROM. It clearly says in the datasheet that the ACK status can be found at SSPCON2.ACKSTAT. So my guess was to poll this bit until the slave responds to my data, but the program hangs in the while Loop.
void vReadACK (void)
{
while (SSPCON2.ACKSTAT != 0);
}
And here's my write function, my I2CCheck function and I2C Master Initialization function
void vI2CEcrireOctet (UC ucData, UC ucRW)
{
vI2CCheck();
switch (ucRW)
{
case READ:
SSPBUF = ucData + 1;
break;
case WRITE:
SSPBUF = ucData + 0;
break;
}
vReadACK();
}
void vI2CCheck (void)
{
while (SSPCON2.ACKEN); //ACKEN not cleared, wait
while (SSPCON2.RCEN); //RCEN not cleared, wait
while (SSPCON2.PEN); //STOP not cleared, wait
while (SSPCON2.SEN); //Start not cleared, wait
while (SSPCON2.RSEN); //Rep start not cleared, wait
while (SSP1STAT.R_NOT_W); //TX not done wait
}
void vInitI2CMaster (void)
{
TRISB4_bit = 1; //SDA IN
TRISB6_bit = 1; //SCL IN
SSP1STAT.SMP = 1; //No slew rate
SSP1STAT.CKE = 0; //Disable SMBus inputs
SSPADD = 0x27; //100 KHz
SSPCON1 = 0b00101000; //I2C Master mode
SSPCON3 = 0b00000000; //Rien de slave
}
Just so you know, 24LC32A WriteProtect tied to VSS, A2-A1-A0 tied to GND, so adress 0xA0. 4k7 pull-ups are on I2C line. PIC16F1459 at 16MHz INTOSC.
I'm completely stuck. I've went through the MSSP datasheet 5 to 6 times without finding any issue. Can you guys help?
And here's my logic analyzer preview (removing the while inside vReadAck() )
Well it looks like I've found the answer to my question. What I was doing was the exact way of doing this. The problem seemed to be the Bus Free Time delay required for the slave to respond. At 16Mhz, my I2C was probably too fast for the EEPROM memory. So I've added a small Delay function right after the stop operation, so the write sequences are delayed and BAM, worked.
Bus free time: Time the bus
must be free before a new
transmission can start.
Despite the fact you "totally know" know "PIC won't ACK the data I send to the I2C EEPROM" because it's not supposed to, you still seem to misunderstand how I2C acknowledgements are supposed to work. They're called acknowledgements because they can be both positively (ACK) and negatively (NAK) acknowledged. If you look at the the analyzer screen shot you posted you'll find that its quite clearly labelled each byte being sent as having been NAK'ed by the transmitter.
To properly check for I2C ACKs you should be polling the trailing edge of the ACKTIM bit, and then checking the ACKSTAT bit to find out whether the slave transmitted an ACK or a NAK bit. Something like this:
int
vReadACK() {
while(!SSPCON3.ACKTIM);
while(SSPCON3.ACKTIM);
return SSPCON2.ACKSTAT;
}
As for why your slaved device is apparently NAKing each byte it isn't clear from the code you've posted, but there's a couple of notable omissions from your code. You need to generate start and stop conditions but you've shown no code to do this.