is it a kernel freeze - c

We are missing interrupts in an linux embedded system having multi-core running at 1.25GHz.
Background:
kernel version: 2.6.32.27
we have user space processes which need real time performance.
They operate in a 1ms boundary.
That is to say within 1ms they are expected to complete a set of task, which at the max may take about 800uS.
We have a external component FPGA which provides, 1ms and 10ms interrupts to the multi-core processor through GPIO pins configured as edge triggerred interrupts.
These interrupts are handled in kernel driver.
The software architecture is in such a way that the user process, after completing its work will do an ioctl to the GPIO driver.
In this ioctl the driver will put the process to wakeup_interruptible state.
Whenever the next 1ms interrupt is received, the ISR will wakeup the process. This cycle repeats.
Both the 1ms and 10ms interrupts are routed to a single core of the processor using smp_affinity.
Problem:
Sometimes we find that some interrupts are missed.
(ie ISR itself doesnt get invoked).
After 12 to 20ms ISR's are hit normally.
This we are able to understand by profiling the duration between consecutive ISR calls, and having counters incremented first thing in the ISR.
This mostly happens during high system load at process level, and is random and hard to reproduce.
I have attached the skeletal code.
First I have to isolate whether it is a hardware or software problem. As it is a FPGA which is giving the interrupts, we dont have much doubt on the hardware.
Is this kernel freezing? It is the most likely case since the cpu cycles are incrementing.
Can it be a case of cpu freeze due to thermal conditions? If so, then the cpu cycles wouldn't have incremented in first place.
Any pointers to debug/isolate the root cause will be of great help
considering the kernel version we are working on and the profiling/debugging
facilities available in this kernel version.
skeletal code:
/* Build time Configuration */
/* Macros */
DECLARE_WAIT_QUEUE_HEAD(wait);
/** Structure Definitions */
/** Global Variables */
gpio_dev_t gpio1msDev, gpio10msDev;
GpioIntProfileSectorData_t GpioSigProfileData[MAX_GPIO_INT_CONSUMERS];
GpioIntProfileSectorData_t *ProfilePtrSector;
GpioIntProfileData_t GpioProfileData;
GpioIntProfileData_t *GpioIntProfilePtr;
CurrentTickProfile_t TimeStamp;
uint64_t ModuleInitDone = 0, FirstTimePIDWrite = 0;
uint64_t PrevCycle = 0, NowCycle = 0;
volatile uint64_t TenMsFlag, OneMsFlag;
uint64_t OneMsCounter;
uint64_t OneMsIsrTime, TenMsIsrTime;
uint64_t OneMsCounter, OneMsTime, TenMsTime, SyncStarted;
uint64_t Prev = 0, Now = 0, DiffTen = 0, DiffOne, SesSyncHappened;
static spinlock_t GpioSyncLock = SPIN_LOCK_UNLOCKED;
static spinlock_t IoctlSyncLock = SPIN_LOCK_UNLOCKED;
uint64_t EventPresent[MAX_GPIO_INT_CONSUMERS];
GpioEvent_t CurrentEvent = KERN_NO_EVENT;
TickSyncSes_t *SyncSesPtr = NULL;
/** Function Declarations */
ssize_t write_pid(struct file *filep, const char __user * buf, size_t count, loff_t * ppos);
long Gpio_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
static const struct file_operations my_fops = {
write:write_pid,
compat_ioctl:Gpio_compat_ioctl,
};
/**
* IOCTL function for GPIO interrupt module
*
* #return
*/
long Gpio_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
int len = 1, status = 0;
uint8_t Instance;
uint64_t *EventPtr;
GpioIntProfileSectorData_t *SectorProfilePtr, *DebugProfilePtr;
GpioEvent_t EventToGive = KERN_NO_EVENT;
pid_t CurrentPid = current->pid;
spin_lock(&IoctlSyncLock); // Take the spinlock
Instance = GetSector(CurrentPid);
SectorProfilePtr = &GpioSigProfileData[Instance];
EventPtr = &EventPresent[Instance];
spin_unlock(&IoctlSyncLock);
if (Instance <= MAX_GPIO_INT_CONSUMERS)
{
switch (cmd)
{
case IOCTL_WAIT_ON_EVENT:
if (*EventPtr)
{
/* Dont block here since this is a case where interrupt has happened
* before process calling the polling API */
*EventPtr = 0;
/* some profiling code */
}
else
{
status = wait_event_interruptible(wait, (*EventPtr == 1));
*EventPtr = 0;
}
/* profiling code */
TimeStamp.CurrentEvent = EventToGive;
len = copy_to_user((char *)arg, (char *)&TimeStamp, sizeof(CurrentTickProfile_t));
break;
default:
break;
}
}
else
{
return -EINVAL;
}
return 0;
}
/**
* Send signals to registered PID's.
*
* #return
*/
static void WakeupWaitQueue(GpioEvent_t Event)
{
int i;
/* some profile code */
CurrentEvent = Event;
// we dont wake up debug app hence "< MAX_GPIO_INT_CONSUMERS" is used in for loop
for (i = 0; i < MAX_GPIO_INT_CONSUMERS; i++)
{
EventPresent[i] = 1;
}
wake_up_interruptible(&wait);
}
/**
* 1ms Interrupt handler
*
* #return
*/
static irqreturn_t gpio_int_handler_1ms(int irq, void *irq_arg)
{
uint64_t reg_read, my_core_num;
unsigned long flags;
GpioEvent_t event = KERN_NO_EVENT;
/* code to clear the interrupt registers */
/************ profiling start************/
NowCycle = get_cpu_cycle();
GpioIntProfilePtr->TotalOneMsInterrupts++;
/* Check the max diff between consecutive interrupts */
if (PrevCycle)
{
DiffOne = NowCycle - PrevCycle;
if (DiffOne > GpioIntProfilePtr->OneMsMaxDiff)
GpioIntProfilePtr->OneMsMaxDiff = DiffOne;
}
PrevCycle = NowCycle;
TimeStamp.OneMsCount++; /* increment the counter */
/* Store the timestamp */
GpioIntProfilePtr->Gpio1msTimeStamp[GpioIntProfilePtr->IndexOne] = NowCycle;
TimeStamp.OneMsTimeStampAtIsr = NowCycle;
GpioIntProfilePtr->IndexOne++;
if (GpioIntProfilePtr->IndexOne == GPIO_PROFILE_ARRAY_SIZE)
GpioIntProfilePtr->IndexOne = 0;
/************ profiling end************/
/*
* Whenever 10ms Interrupt happens we send only one event to the upper layers.
* Hence it is necessary to sync between 1 & 10ms interrupts.
* There is a chance that sometimes 1ms can happen at first and sometimes 10ms.
*
*/
/******** Sync mechanism ***********/
spin_lock_irqsave(&GpioSyncLock, flags); // Take the spinlock
OneMsCounter++;
OneMsTime = NowCycle;
DiffOne = OneMsTime - TenMsTime;
if (DiffOne < MAX_OFFSET_BETWEEN_1_AND_10MS) //ten ms has happened first
{
if (OneMsCounter == 10)
{
event = KERN_BOTH_EVENT;
SyncStarted = 1;
}
else
{
if (SyncStarted)
{
if (OneMsCounter < 10)
{
GpioIntProfilePtr->TickSyncErrAt1msLess++;
}
else if (OneMsCounter > 10)
{
GpioIntProfilePtr->TickSyncErrAt1msMore++;
}
}
}
OneMsCounter = 0;
}
else
{
if (OneMsCounter < 10)
{
if (SyncStarted)
{
event = KERN_ONE_MS_EVENT;
}
}
else if (OneMsCounter > 10)
{
OneMsCounter = 0;
if (SyncStarted)
{
GpioIntProfilePtr->TickSyncErrAt1msMore++;
}
}
}
TimeStamp.SFN = OneMsCounter;
spin_unlock_irqrestore(&GpioSyncLock, flags);
/******** Sync mechanism ***********/
if(event != KERN_NO_EVENT)
WakeupWaitQueue(event);
OneMsIsrTime = get_cpu_cycle() - NowCycle;
if (GpioIntProfilePtr->Max1msIsrTime < OneMsIsrTime)
GpioIntProfilePtr->Max1msIsrTime = OneMsIsrTime;
return IRQ_HANDLED;
}
/**
* 10ms Interrupt handler
*
* #return
*/
static irqreturn_t gpio_int_handler_10ms(int irq, void *irq_arg)
{
uint64_t reg_read, my_core_num;
unsigned long flags;
GpioEvent_t event = KERN_NO_EVENT;
/* clear the interrupt */
/************ profiling start************/
GpioIntProfilePtr->TotalTenMsInterrupts++;
Now = get_cpu_cycle();
if (Prev)
{
DiffTen = Now - Prev;
if (DiffTen > GpioIntProfilePtr->TenMsMaxDiff)
GpioIntProfilePtr->TenMsMaxDiff = DiffTen;
}
Prev = Now;
TimeStamp.OneMsCount++; /* increment the counter */
TimeStamp.TenMsCount++;
GpioIntProfilePtr->Gpio10msTimeStamp[GpioIntProfilePtr->IndexTen] = Now;
TimeStamp.TenMsTimeStampAtIsr = Now;
//do_gettimeofday(&TimeOfDayAtIsr.TimeAt10MsIsr);
GpioIntProfilePtr->IndexTen++;
if (GpioIntProfilePtr->IndexTen == GPIO_PROFILE_ARRAY_SIZE)
GpioIntProfilePtr->IndexTen = 0;
/************ profiling end************/
/******** Sync mechanism ***********/
spin_lock_irqsave(&GpioSyncLock, flags);
TenMsTime = Now;
DiffTen = TenMsTime - OneMsTime;
if (DiffTen < MAX_OFFSET_BETWEEN_1_AND_10MS) //one ms has happened first
{
if (OneMsCounter == 10)
{
TimeStamp.OneMsTimeStampAtIsr = Now;
event = KERN_BOTH_EVENT;
SyncStarted = 1;
}
OneMsCounter = 0;
}
else
{
if (SyncStarted)
{
if (OneMsCounter < 9)
{
GpioIntProfilePtr->TickSyncErrAt10msLess++;
OneMsCounter = 0;
}
else if (OneMsCounter > 9)
{
GpioIntProfilePtr->TickSyncErrAt10msMore++;
OneMsCounter = 0;
}
}
else
{
if (OneMsCounter != 9)
OneMsCounter = 0;
}
}
TimeStamp.SFN = OneMsCounter;
spin_unlock_irqrestore(&GpioSyncLock, flags);
/******** Sync mechanism ***********/
if(event != KERN_NO_EVENT)
WakeupWaitQueue(event);
TenMsIsrTime = get_cpu_cycle() - Now;
if (GpioIntProfilePtr->Max10msIsrTime < TenMsIsrTime)
GpioIntProfilePtr->Max10msIsrTime = TenMsIsrTime;
return IRQ_HANDLED;
}

Reseting EventPresent after waiting the event in wait_event_interruptible()
EventPtr = &EventPresent[Instance];
...
status = wait_event_interruptible(wait, (*EventPtr == 1));
*EventPtr = 0;
looks suspicious.
If WakeupWaitQueue() will be executed concurrently, then setting of the event
for (i = 0; i < MAX_GPIO_INT_CONSUMERS; i++)
{
EventPresent[i] = 1;
}
wake_up_interruptible(&wait);
will be lost.
It is better to have two independent counters for raised events and for processed events:
uint64_t EventPresent[MAX_GPIO_INT_CONSUMERS]; // Number if raised events
uint64_t EventProcessed[MAX_GPIO_INT_CONSUMERS]; // Number of processed events
In that case condition could be a comparision of these counters:
Gpio_compat_ioctl()
{
...
EventPresentPtr = &EventPresent[Instance];
EventProcessedPtr = &EventProcessed[Instance];
...
status = wait_event_interruptible(wait, (*EventPresentPtr != *EventProcessedPtr));
(*EventProcessedPtr)++;
...
}
WakeupWaitQueue()
{
...
for (i = 0; i < MAX_GPIO_INT_CONSUMERS; i++)
{
EventPresent[i]++;
}
wake_up_interruptible(&wait);
}

This was not a kernel freeze.
We had a free core in the system which was running baremetal. We routed the 1ms interrupts to this baremetal core as well. When the issue occurs, we compared with the baremetal core profile info. In baremetal core ISRs were hit properly linear to the time elapsed.
By this we ruled out that there are not HW issues or thermal issues.
Next on close look of the code, we started suspecting whether spinlock is causing to miss the interrupts. To experiment, we changed the logic to run the ISRs without spinlock. Now we see that there are not missed interrupts.
So the issues seems solved, however with spinlock present also the system was working properly under normal load conditions. This issue arises only during very high CPU load. This is something for which i dont have an answer though ie only during high load condition, why calling spinlock makes the other interrupt to be missed.

Related

How to update the LED on a TI board and report to the server every second via UART

I am modifying a code in embedded C language and trying to make it to where it checks buttons every 200ms, checks the temperature every 500ms, and update the LED and report to the server every second. My code appears to be complete but when I run it, nothing outputs to the console, and my LED light neither turns off or on when I press either buttons on the side of the TI board. Is there something wrong with my nested while loop? Here is my code:
/*
* ======== gpiointerrupt.c ========
*/
#include <stdint.h>
#include <stddef.h>
/* Driver Header files */
#include <ti/drivers/GPIO.h>
#include <ti/drivers/I2C.h>
#include <ti/drivers/UART.h>
/* Driver configuration */
#include "ti_drivers_config.h"
/* Driver timer */
#include <ti/drivers/Timer.h>
#define TRUE 1
#define FALSE 0
#define NUMBER_OF_TASKS 3
#define GLOBAL_PERIOD 100 //milliseconds
#define DISPLAY(x) UART_write(uart, &output, x);
// UART Global Variables
char output[64];
int bytesToSend;
// Driver Handles - Global variables
UART_Handle uart;
// Init variables
int buttonCheckTime = 0;
int tempCheckTime = 0;
int displayCheckTime = 0;
int buttonCheckPeriod = 200;
int tempCheckPeriod = 500;
int displayCheckPeriod = 1000;
int setpoint = 25;
int heat = 0;
int seconds = 0;
int temperature = 0;
int firstButtonWasPressed = FALSE; // It is initally false that the button was pressed
int secondButtonWasPressed = FALSE; // It is initally false that the button was pressed
int global_period = GLOBAL_PERIOD; // Global period to be used initTimer()
void initUART(void)
{
UART_Params uartParams;
// Init the driver
UART_init();
// Configure the driver
UART_Params_init(&uartParams);
uartParams.writeDataMode = UART_DATA_BINARY;
uartParams.readDataMode = UART_DATA_BINARY;
uartParams.readReturnMode = UART_RETURN_FULL;
uartParams.baudRate = 115200;
// Open the driver
uart = UART_open(CONFIG_UART_0, &uartParams);
if (uart == NULL)
{
/* UART_open() failed */
while (1)
;
}
}
// I2C Global Variables
static const struct
{
uint8_t address;
uint8_t resultReg;
char *id;
} sensors[3] = { { 0x48, 0x0000, "11X" }, { 0x49, 0x0000, "116" }, { 0x41,
0x0001,
"006" } };
uint8_t txBuffer[1];
uint8_t rxBuffer[2];
I2C_Transaction i2cTransaction;
// Driver Handles - Global variables
I2C_Handle i2c;
// Initialize the I2C peripheral
// Make sure you call initUART() before calling this function.
void initI2C(void)
{
int8_t i, found;
I2C_Params i2cParams;
DISPLAY(snprintf(output, 64, "Initializing I2C Driver - "))
// Init the driver
I2C_init();
// Configure the driver
I2C_Params_init(&i2cParams);
i2cParams.bitRate = I2C_400kHz;
// Open the driver
i2c = I2C_open(CONFIG_I2C_0, &i2cParams);
if (i2c == NULL)
{
DISPLAY(snprintf(output, 64, "Failed\n\r"))
while (1)
;
}
DISPLAY(snprintf(output, 32, "Passed\n\r"))
// Boards were shipped with different sensors.
// Welcome to the world of embedded systems.
// Try to determine which sensor we have.
// Scan through the possible sensor addresses
/* Common I2C transaction setup */
i2cTransaction.writeBuf = txBuffer;
i2cTransaction.writeCount = 1;
i2cTransaction.readBuf = rxBuffer;
i2cTransaction.readCount = 0;
found = false;
for (i = 0; i < 3; ++i)
{
i2cTransaction.slaveAddress = sensors[i].address;
txBuffer[0] = sensors[i].resultReg;
DISPLAY(snprintf(output, 64, "Is this %s? ", sensors[i].id))
if (I2C_transfer(i2c, &i2cTransaction))
{
DISPLAY(snprintf(output, 64, "Found\n\r"))
found = true;
break;
}
DISPLAY(snprintf(output, 64, "No\n\r"))
}
if (found)
{
DISPLAY(snprintf(output, 64, "Detected TMP%s I2C address: %x\n\r",
sensors[i].id, i2cTransaction.slaveAddress))
}
else
{
DISPLAY(snprintf(output, 64,
"Temperature sensor not found, contact professor\n\r"))
}
}
int16_t readTemp(void)
{
int j;
int16_t temperature = 0;
i2cTransaction.readCount = 2;
if (I2C_transfer(i2c, &i2cTransaction))
{
/*
* Extract degrees C from the received data;
* see TMP sensor datasheet
*/
temperature = (rxBuffer[0] << 8) | (rxBuffer[1]);
temperature *= 0.0078125;
/*
* If the MSB is set '1', then we have a 2's complement
* negative value which needs to be sign extended
*/
if (rxBuffer[0] & 0x80)
{
temperature |= 0xF000;
}
}
else
{
DISPLAY(snprintf(output, 64, "Error reading temperature sensor (%d)\n\r", i2cTransaction.status))
DISPLAY(snprintf(output,64, "Please power cycle your board by unplugging USB and plugging back in.\n\r"))
}
return temperature;
}
// Driver Handles - Global variables
Timer_Handle timer0;
volatile unsigned char TimerFlag = 0;
// A single task in the task list.
struct task_entry
{
void (*f)(); // Function to call to perform the task
int elapsed_time; // Amount of time since last triggered
int period; // Period of the task in ms
char triggered; // Whether or not the task was triggered
};
// Forward declaration
void task_one();
void task_two();
void task_three();
void task_one()
{
// Processing for task_one takes place
/* Every 200ms, check button presses */
if (buttonCheckTime >= buttonCheckPeriod) // Button check time equals or exceeds period
{
if (firstButtonWasPressed == TRUE) // Button on one side raises setpoint (thermostat setting) by 1
{
setpoint += 1; // Increment thermostat
firstButtonWasPressed = FALSE; // Reset button to FALSE
}
if (secondButtonWasPressed == TRUE) // Button on the other side lowers setpoint (thermostat setting) by 1
{
setpoint -= 1; // Decrement thermostat
secondButtonWasPressed = FALSE; // Reset button to FALSE
}
}
}
void task_two()
{
// Processing for task_two takes place
if (tempCheckTime >= tempCheckPeriod) // Temperature check time equals or exceeds period
{
temperature = readTemp();
if (temperature > setpoint) {
GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_OFF);
heat = 0;
}
else {
GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_ON);
heat = 1;
}
}
}
void task_three()
{
// Processing for task_three takes place
if (displayCheckTime == displayCheckPeriod) {
DISPLAY(snprintf(output, 64, "<%02d,%02d,%d,%04d>\n\r", temperature,
setpoint, heat, seconds));
++seconds;
}
}
// The task list
struct task_entry tasks[NUMBER_OF_TASKS] = { { FALSE, &task_one, 500, 500 }, {FALSE, &task_two, 1500, 1500 }, { FALSE, &task_three, 2000, 2000 } };
void timerCallback(Timer_Handle myHandle, int_fast16_t status)
{
int x = 0;
// Walk through each task.
for (x = 0; x < NUMBER_OF_TASKS; x++)
{
// Check if task's interval has expire
if (tasks[x].elapsed_time >= tasks[x].period)
{
// Bing! This task's timer is up
// Set it's flag, and the global flag
tasks[x].triggered = TRUE;
TimerFlag = TRUE;
// Reset the elapsed_time
tasks[x].elapsed_time = 0;
}
else
{
tasks[x].elapsed_time += global_period;
}
}
}
void initTimer(void)
{
Timer_Params params;
// Init the driver
Timer_init();
// Configure the driver
Timer_Params_init(&params);
params.period = 1000000;
params.periodUnits = Timer_PERIOD_US;
params.timerMode = Timer_CONTINUOUS_CALLBACK;
params.timerCallback = timerCallback;
// Open the driver
timer0 = Timer_open(CONFIG_TIMER_0, &params);
if (timer0 == NULL)
{
/* Failed to initialized timer */
while (1)
{
}
}
if (Timer_start(timer0) == Timer_STATUS_ERROR)
{
/* Failed to start timer */
while (1)
{
}
}
}
/*
* ======== gpioButtonFxn0 ========
* Callback function for the GPIO interrupt on CONFIG_GPIO_BUTTON_0.
*
* Note: GPIO interrupts are cleared prior to invoking callbacks.
*/
void gpioButtonFxn0(uint_least8_t index)
{
/* Toggle an LED */
//GPIO_toggle(CONFIG_GPIO_LED_0);
firstButtonWasPressed = TRUE; // It is true that the button was pressed
}
/*
* ======== gpioButtonFxn1 ========
* Callback function for the GPIO interrupt on CONFIG_GPIO_BUTTON_1.
* This may not be used for all boards.
*
* Note: GPIO interrupts are cleared prior to invoking callbacks.
*/
void gpioButtonFxn1(uint_least8_t index)
{
/* Toggle an LED */
//GPIO_toggle(CONFIG_GPIO_LED_1);
secondButtonWasPressed = TRUE;
}
/*
* ======== mainThread ========
*/
void* mainThread(void *arg0)
{
/* Call driver init functions */
GPIO_init();
/* Configure the LED and button pins */
GPIO_setConfig(CONFIG_GPIO_LED_0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
//GPIO_setConfig(CONFIG_GPIO_BUTTON_0;
//GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_FALLING);
/* Turn on user LED */
GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_ON);
/* Install Button callback */
GPIO_setCallback(CONFIG_GPIO_BUTTON_0, gpioButtonFxn0);
/* Enable interrupts */
GPIO_enableInt(CONFIG_GPIO_BUTTON_0);
/*
* If more than one input pin is available for your device, interrupts
* will be enabled on CONFIG_GPIO_BUTTON1.
*/
if (CONFIG_GPIO_BUTTON_0 != CONFIG_GPIO_BUTTON_1)
{
/* Configure BUTTON1 pin */
GPIO_setConfig(CONFIG_GPIO_BUTTON_1,
GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_FALLING);
/* Install Button callback */
GPIO_setCallback(CONFIG_GPIO_BUTTON_1, gpioButtonFxn1);
/* Enable interrupts */
GPIO_enableInt(CONFIG_GPIO_BUTTON_1);
}
// Call driver init functions
initUART(); // The UART must be initialized before calling initI2C()
initI2C();
initTimer();
// Loop Forever
while (TRUE)
{
readTemp();
// Every 200ms check the button flags
task_one();
// Every 500ms read the temperature and update the LED
task_two();
// Every second output the following to the UART
// "<%02d,%02d,%d,%04d>, temperature, setpoint, heat, seconds
task_three();
// Wait for task intervals (periods) to elapse
while (!TimerFlag)
{
} // Wait for timer period
// Process the tasks for which the interval has expired
int x = 0;
for (x = 0; x < NUMBER_OF_TASKS; x++)
{
if (tasks[x].triggered)
{
tasks[x].f();
// reset
tasks[x].triggered = FALSE;
}
}
// Reset everything (e.g. flags) and go back to the beginning
TimerFlag = FALSE; // Lower flag raised by timer
++global_period;
}
}

is there a HAL_SPI_transmit function in stm8?

I'm trying to write a stm32 code in stm8. The problem is I can't find a SPI_Transmit function only SPI_SendData. I need this function to transmit an array through SPI.
The stm32 SPI_transmit function looks like this:
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint32_t tickstart;
HAL_StatusTypeDef errorcode = HAL_OK;
uint16_t initial_TxXferCount;
/* Check Direction parameter */
assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));
/* Process Locked */
__HAL_LOCK(hspi);
/* Init tickstart for timeout management*/
tickstart = HAL_GetTick();
initial_TxXferCount = Size;
if (hspi->State != HAL_SPI_STATE_READY)
{
errorcode = HAL_BUSY;
goto error;
}
if ((pData == NULL) || (Size == 0U))
{
errorcode = HAL_ERROR;
goto error;
}
/* Set the transaction information */
hspi->State = HAL_SPI_STATE_BUSY_TX;
hspi->ErrorCode = HAL_SPI_ERROR_NONE;
hspi->pTxBuffPtr = (uint8_t *)pData;
hspi->TxXferSize = Size;
hspi->TxXferCount = Size;
/*Init field not used in handle to zero */
hspi->pRxBuffPtr = (uint8_t *)NULL;
hspi->RxXferSize = 0U;
hspi->RxXferCount = 0U;
hspi->TxISR = NULL;
hspi->RxISR = NULL;
/* Configure communication direction : 1Line */
if (hspi->Init.Direction == SPI_DIRECTION_1LINE)
{
/* Disable SPI Peripheral before set 1Line direction (BIDIOE bit) */
__HAL_SPI_DISABLE(hspi);
SPI_1LINE_TX(hspi);
}
#if (USE_SPI_CRC != 0U)
/* Reset CRC Calculation */
if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
SPI_RESET_CRC(hspi);
}
#endif /* USE_SPI_CRC */
/* Check if the SPI is already enabled */
if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
{
/* Enable SPI peripheral */
__HAL_SPI_ENABLE(hspi);
}
/* Transmit data in 16 Bit mode */
if (hspi->Init.DataSize == SPI_DATASIZE_16BIT)
{
if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
{
hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint16_t);
hspi->TxXferCount--;
}
/* Transmit data in 16 Bit mode */
while (hspi->TxXferCount > 0U)
{
/* Wait until TXE flag is set to send data */
if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
{
hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint16_t);
hspi->TxXferCount--;
}
else
{
/* Timeout management */
if ((((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
{
errorcode = HAL_TIMEOUT;
goto error;
}
}
}
}
/* Transmit data in 8 Bit mode */
else
{
if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
{
*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint8_t);
hspi->TxXferCount--;
}
while (hspi->TxXferCount > 0U)
{
/* Wait until TXE flag is set to send data */
if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
{
*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint8_t);
hspi->TxXferCount--;
}
else
{
/* Timeout management */
if ((((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
{
errorcode = HAL_TIMEOUT;
goto error;
}
}
}
}
#if (USE_SPI_CRC != 0U)
/* Enable CRC Transmission */
if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
}
#endif /* USE_SPI_CRC */
/* Check the end of the transaction */
if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK)
{
hspi->ErrorCode = HAL_SPI_ERROR_FLAG;
}
/* Clear overrun flag in 2 Lines communication mode because received is not read */
if (hspi->Init.Direction == SPI_DIRECTION_2LINES)
{
__HAL_SPI_CLEAR_OVRFLAG(hspi);
}
if (hspi->ErrorCode != HAL_SPI_ERROR_NONE)
{
errorcode = HAL_ERROR;
}
error:
hspi->State = HAL_SPI_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hspi);
return errorcode;
}
This is what the SPI_SendData function looks like:
void SPI_SendData(SPI_TypeDef* SPIx, uint8_t Data)
{
SPIx->DR = Data; /* Write in the DR register the data to be sent*/
}
Stm8 doesn't have HAL functions. I think I have to define a completely new function for this to work in stm8, but I'm not sure. Is there another way?
I think I have to define a completely new function for this to work in stm8, but I'm not sure.
Yes very likely a completely new function/driver. STM8 is an entirely different architecture compared to STM32. If you are lucky some STM8 hardware peripherals were re-used in STM32, but I doubt it since STM8 is a way older legacy architecture. For example I would guess that it only supports 8 bit mode (which is the common implementation).
The way to actually learn microcontroller programming proper, is to learn how to write drivers yourself instead of relying on various bloatware libs such as the horrible, so-called "HAL" by ST.
SPI is an excellent peripheral to use when practicing this, since it is relatively simple hardware. Some tricky things to look out for:
SPI does not transmit or receive, it transceives in full duplex.
Flag clearing in SPI peripherals is often done automatically, for example by reading a status register followed by reading a data register. This can sometimes mess up debuggers that display registers or a raw memory map, since the debugger might get the idea to read registers when you single step and thereby destroy the flags.
/SS chip select might sometimes be handled by the SPI hardware, sometimes you have to handle it manually with GPIO. Also some devices might have peculiar timing requirements forcing you to handle things manually.
You can tailor the driver for a specific application or make it more generic, at your own preference.

How can I add a delay of 90 minutes when a port has gone from 0 to 1?

I have a PIC12F1822 of which there is an LED and relay as an output and a trigger as an input.
When the trigger is equal to one, I would like the to have the LED and relay stay turned on for 90 minutes.
The problem is that I cannot use a delay since __delayms has a limit.
How can this be done?
#include <xc.h>
#define _XTAL_FREQ 4000000
#define LED PORTAbits.RA5
#define RELAY PORTAbits.RA4
#define TRIGGER PORTAbits.RA2
int main()
{
// OSCCON = 0b01101000 ;
OSCCONbits.IRCF = 0b1101 ;
TRISAbits.TRISA5 = 0; //LED Output PIN
TRISAbits.TRISA4 = 0; //Output for Relay
TRISAbits.TRISA2 = 1; //INPUT trigger from comparator
while(1)
{
if (TRIGGER == 1)
{
LED = 1; // LED ON
__delay_ms(1000); // 1 Second Delay
LED = 0; // LED OFF
__delay_ms(1000); // 1 Second Delay
}
else
{
LED = 0; // LED OFF
}
}
return 0;
}
why not wrap the 1s delay in a function providing a delay_s(uint32_t seconds) and in a function providing a delay_m(uint32_t minutes)
void delay_s(uint32_t seconds)
{
while(seconds){
__delay_ms(1000);
seconds--;
}
}
void delay_m(uint32_t minutes)
{
while(minutes){
delay_s(60);
minutes--;
}
}
But:
You should be aware that this totally blocks your µC's program.
It will not react on any key press. This may be ok but may be you want the delay to be reset to 90min if the key is pressed during the delay.
I would suggest to implement the whole thing with interrupts and timers.
Set up a timer that triggers once every second and countdown a global variable (volatile!) within the isr that was set by your main loop. If the counter reaches 0 your isr may disable your output.
This way your µC can process other things in the mainloop and is not blocked by a __delay-function.
Simple. State machines and timer interrupts and a minute countdown. Something like this:
Disclaimer - coded blindly
#define MINUTES_90 5400UL /* 90 seconds */
typedef enum
{
/** Code operational */
p_state_idle,
/** Code waiting for 90 minutes */
p_state_waiting
}p_state_t;
static unsigned long gSecondsRemaining = MINUTES_90;
int main()
{
p_state_t gState = p_state_running;
OPTION_REGbits.PSA = 0; /* Prescaler assigned */
OPTION_REGbits.PS = 0b111; /* 256 prescaler */
OPTION_REGbits.TMR0CS = 0; /* Fosc / 4 */
INTCONbits.TMR0IE = 1; /* Timer 0 interrupt enabled */
INTCONbits.PEIE = 1; /* Peripheral interrupts enabled */
INTCONbits.GIE = 1; /* Global interrupts enabled */
/** Default LED off */
LED = 0;
while (1)
{
switch (gState)
{
case p_state_idle:
if (TRIGGER == 1)
{
LED = 1; // LED ON
gSecondsRemaining = MINUTES_90; /* Reset timer countdown */
gState = p_state_waiting;
}
break;
case p_state_waiting:
/** can sleep here */
if (gSecondsRemaining == 0)
{
gState = p_state_idle;
LED = 0;
}
break;
}
}
}
void interrupt ISR()
{
static unsigned char gSecond = 15;
/** approx 15 Hz ? */
if (INTCONbits.TMR0IF)
{
INTCONbits.TMR0IF = 0;
if (gSecond > 0)
gSecond--;
if (gSecond == 0)
{
if (gSecondsRemaining > 0)
gSecondsRemaining--;
gSecond = 15;
}
}
}
You could solve this with an inline assembly function with some loops. Have a look here: example: 30 min delay

STM32F103 Timer IRQ handler never executes

I'm trying to setup a timer that executes every 1ms, in order to estimate the execution time of a function. I attach the code at the end of the post. I'm pretty sure about the configuration of the timer, since I have done only very little modification to a working demo (iNemo_AHRS_continous demo, even the function for prvFindFactors is taken by such a demo).
Unfortunately, in this case my ISR is never executed, any idea of the reason? Thanks
void iNemoProfilerConfig(void)
{
unsigned short a;
unsigned short b;
unsigned long n;
/* This value is the frequency interrupts in Hz */
unsigned short frequency = 1000;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable timer clocks */
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM4, ENABLE );
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
TIM_TimeBaseStructInit( &TIM_TimeBaseStructure );
/* Time base configuration for timer 2 - which generates the interrupts. */
n = SystemCoreClock/frequency;
prvFindFactors( n, &a, &b );
TIM_TimeBaseStructure.TIM_Period = b - 1;
TIM_TimeBaseStructure.TIM_Prescaler = a - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM4, &TIM_TimeBaseStructure );
TIM_ARRPreloadConfig( TIM4, ENABLE );
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
}
void Enable_Timer4(FunctionalState command) {
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
TIM_ITConfig( TIM4, TIM_IT_Update, command );
}
void TIM4_IRQHandler(void)
{
USART1_Printf("INTERRUPT = %i \r\n", counter);
if(TIM_GetITStatus(TIM4, TIM_IT_Update))
{
//xTim2Raised=SET;
counter++;
/* Clear the IRQ bit */
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
}
//main chunk
int main(void)
{
SystemInit();
/* At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup
file (startup_stm32f10x_xx.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_stm32f10x.c file
*/
USART1_Init();
for(uint32_t i=0; i<0xFFFFF;i++);
iNemoTimerConfig();
iNemoProfilerConfig();
iNEMO_AHRS_Start();
LSM_Start();
L3G_Start();
/* Wait some seconds in order to ensure the user opens the VCOM terminal */
for(uint32_t i=0;i<0x1FFFFFF;i++);
Enable_Timer(ENABLE);
Enable_Timer4(ENABLE);
while(1)
{
counter = 0;
iNEMO_AHRS_Attitude();
USART1_Printf("counter = %i \r\n", counter);

How to synchronize between threads out of which one is a timer using pthread?

I want to make a process with 3 threads. Out of which, I want one thread to work once in every 50ms. So made 2 threads to do my other works and in the third thread I initialised a timer. When I did so the synchronisation between the threads doest seem that good. I cant find the timer codes executing in every 50ms. Its random in nature. The code brief is shown below. Thanks in advance.
void * vUserInterfaceThread()
{
while(1)
{
//***doing my interface code here***********/
}
}
void * vMornitorThread()
{
while(1)
{
//***doing my monitor code here***********/
}
}
void * vTimerThread()
{
vStartTimer(ENABLE); // enabled the timer with 50ms delay with the function
while(1);
}
void vTimerFunction()
{
//******Code to be executed in every 50ms time duration here************//
}
void vStartTimer(unsigned char ucValue)
{
if(ucValue == ENABLE)
{
memset (&sSigActionStruct, 0, sizeof (sSigActionStruct));
sSigActionStruct.sa_handler = &vTimerHandler;
sigaction (SIGVTALRM, &sSigActionStruct, NULL);
iTimerValue.it_value.tv_sec = 0;
iTimerValue.it_value.tv_usec = TIMERLOADVALUE; //Load value for 50ms
iTimerValue.it_interval.tv_sec = 0;
iTimerValue.it_interval.tv_usec = TIMERLOADVALUE; //Load value for 50ms
setitimer (ITIMER_VIRTUAL/*ITIMER_REAL*/, &iTimerValue, NULL);
}
}
int main(void)
{
//***************doing other initialisations***************************//
pthread_create(&pThreadID1,NULL,vUserInterfaceThread,NULL);
pthread_create(&pThreadID2,NULL,vMornitorThread,NULL);
pthread_create(&pThreadID3,NULL,vTimerThread,NULL);
pthread_join(pThreadID1,NULL);
pthread_join(pThreadID2,NULL);
pthread_join(pThreadID3,NULL);
}
To answer a part of your question:
If you want to adjust your thread priorities you can use pthread_attr_setschedparam
pthread_attr_t thread_attributes;
pthread_attr_init(&thread_attributes);
struct sched_param params = {.sched_priority = 15}; // Set higher/lower priorities in other threads
pthread_attr_setschedparam(&thread_attributes, &params);
pthread_create(&pThreadID1, &thread_attributes, vUserInterfaceThread, NULL);

Resources