I have made a custom kernel module to handle rising edge triggered interrupts on a GPIO. I want to make a "uio" file for the same in /dev directory. What must I do? Do I need to declare some structures and fill it out ? It would be good if someone could share relevant examples.
Below is the code snippet for the cutom GPIO driver that triggers interrupts.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h> // Required for the GPIO functions
#include <linux/interrupt.h> // Required for the IRQ code
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Khilav Soni");
MODULE_DESCRIPTION("A Switch test driver");
static unsigned int gpio_switch = 65; ///< hard coding the button gpio for this example to P9_27 (GPIO65)
static unsigned int irq_number; ///< Used to share the IRQ number within this file
/// Function prototype for the custom IRQ handler function -- see below for the implementation
static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs);
/** #brief The LKM initialization function
* The static keyword restricts the visibility of the function to within this C file. The __init
* macro means that for a built-in driver (not a LKM) the function is only used at initialization
* time and that it can be discarded and its memory freed up after that point. In this example this
* function sets up the GPIOs and the IRQ
* #return returns 0 if successful
*/
static int __init ebbgpio_init(void){
int result = 0;
printk(KERN_INFO "GPIO_TEST: Initializing the GPIO_TEST LKM\n");
gpio_request(gpio_switch, "sysfs"); // Set up the gpioButton
gpio_direction_input(gpio_switch); // Set the button GPIO to be an input
gpio_set_debounce(gpio_switch, 100); // Debounce the button with a delay of 200ms
//gpio_export(gpio_switch, false); // Causes gpio115 to appear in /sys/class/gpio
// the bool argument prevents the direction from being changed
// Perform a quick test to see that the button is working as expected on LKM load
printk(KERN_INFO "GPIO_TEST: The button state is currently: %d\n", gpio_get_value(gpio_switch));
// GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us
irq_number = gpio_to_irq(gpio_switch);
printk(KERN_INFO "GPIO_TEST: The button is mapped to IRQ: %d\n", irq_number);
// This next call requests an interrupt line
result = request_irq(irq_number, // The interrupt number requested
(irq_handler_t) ebbgpio_irq_handler, // The pointer to the handler function below
IRQF_TRIGGER_RISING, // Interrupt on rising edge (button press, not release)
"switch-event", // Used in /proc/interrupts to identify the owner
NULL); // The *dev_id for shared interrupt lines, NULL is okay
printk(KERN_INFO "GPIO_TEST: The interrupt request result is: %d\n", result);
return result;
}
/** #brief The LKM cleanup function
* Similar to the initialization function, it is static. The __exit macro notifies that if this
* code is used for a built-in driver (not a LKM) that this function is not required. Used to release the
* GPIOs and display cleanup messages.
*/
static void __exit ebbgpio_exit(void){
free_irq(irq_number, NULL); // Free the IRQ number, no *dev_id required in this case
gpio_unexport(gpio_switch); // Unexport the Button GPIO
gpio_free(gpio_switch); // Free the Button GPIO
}
/** #brief The GPIO IRQ Handler function
* This function is a custom interrupt handler that is attached to the GPIO above. The same interrupt
* handler cannot be invoked concurrently as the interrupt line is masked out until the function is complete.
* This function is static as it should not be invoked directly from outside of this file.
* #param irq the IRQ number that is associated with the GPIO -- useful for logging.
* #param dev_id the *dev_id that is provided -- can be used to identify which device caused the interrupt
* Not used in this example as NULL is passed.
* #param regs h/w specific register values -- only really ever used for debugging.
* return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise.
*/
static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){
static unsigned int numberPresses = 0; ///< For information, store the number of button presses
printk(KERN_INFO "GPIO_TEST: Interrupt! (button state is %d)\n", gpio_get_value(gpio_switch));
printk(KERN_INFO "GPIO_TEST: The button was pressed %d times\n", numberPresses);
numberPresses++; // Global counter, will be outputted when the module is unloaded
return (irq_handler_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly
}
/// This next calls are mandatory -- they identify the initialization function
/// and the cleanup function (as above).
module_init(ebbgpio_init);
module_exit(ebbgpio_exit);
Related
The code below is related to the initialization of an RTC in an MCU.
Would anybody know the rational for passing NULL to rtc_init() and then setting a global callback global_rtc_cb equal to it.
Why would you use a global callback at all when there is an other function called rtc_callback defined and used as the callback in the struct.
int main() {
rtc_init(NULL);
}
//-----------------------------------------------------------------
void ( * global_rtc_cb)(void *);
int rtc_init(void (*cb)(void *)) {
rtc_config_t cfg;
cfg.init_val = 0;
cfg.alarm_en = true;
cfg.alarm_val = ALARM;
cfg.callback = rtc_callback;
cfg.callback_data = NULL;
global_rtc_cb = cb;
irq_request(IRQ_RTC_0, rtc_isr_0);
clk_periph_enable(CLK_PERIPH_RTC_REGISTER | CLK_PERIPH_CLK);
rtc_set_config(QM_RTC_0, &cfg);
return 0;
}
//---------------------------------------------------------------------
/**
* RTC configuration type.
*/
typedef struct {
uint32_t init_val; /**< Initial value in RTC clocks. */
bool alarm_en; /**< Alarm enable. */
uint32_t alarm_val; /**< Alarm value in RTC clocks. */
/**
* User callback.
*
* #param[in] data User defined data.
*/
void (*callback)(void *data);
void *callback_data; /**< Callback user data. */
} rtc_config_t;
The rtc_ functions are part of the RTC driver. The RTC driver has something driver-specific to do when the event that prompts the callback occurs. This driver-specific stuff happens in rtc_callback. But there may also be other application-specific stuff that the application must do when the event occurs. The application-specific stuff should be done at the application layer, not within the driver. So if the application has something to do in response to the event it can provide a callback to rtc_init. Surely rtc_callback calls global_rtc_cb so that both the driver-specific stuff and the application-specific stuff is performed when the event occurs. Apparently your particular application doesn't need to do anything for this event so it passes NULL to rtc_init. But a different application that uses the same driver may provide a callback function.
The code below is an example of how to use the real time clock on an mcu.
My question is in relation to callbacks and function pointers.
I have included the struct declaration for rtc_config_t below.
My question is, on the line cfg.callback = rtc_example_callback
Why is the & sign not used before rtc_example_callback.
Why is not necessary to pass the arguments to rtc_example_callback?
The last struct memeber void *callback_data; is set to NULL, I don't understand what this does?
When or what would you want to return?
Many tanks for your inputs
#include "rtc.h"
#include "interrupt.h"
#include "isr.h"
#define ALARM (QM_RTC_ALARM_MINUTE / 6)
#define MAX_RTC_FIRINGS (5)
void rtc_example_callback(void *);
static volatile uint32_t rtc_fired = 0;
/* RTC app example */
int main(void)
{
/* Variables */
rtc_config_t cfg; //create a struct variable to configure the RTC
PRINTF("Starting: RTC\n");
/* Initialise RTC configuration */
cfg.init_val = 0;
cfg.alarm_en = true;
cfg.alarm_val = ALARM;
cfg.callback = rtc_example_callback;
cfg.callback_data = NULL;
irq_request(IRQ_RTC_0, rtc_isr_0); //submit the RTC to the interrupt service routine
clk_periph_enable(CLK_PERIPH_RTC_REGISTER | CLK_PERIPH_CLK); //switch on RTC and Periphal clock
rtc_set_config(RTC_0, &cfg); //Set the RTC configuration
/* Wait for RTC to fire 5 times and then finish. */
while (rtc_fired < MAX_RTC_FIRINGS) {
}
PRINTF("Finished: RTC\n");
clk_periph_disable(CLK_PERIPH_RTC_REGISTER | CLK_PERIPH_CLK); //turn off the clocks
return 0;
}
void rtc_example_callback(void *data)
{
PUTS("Alarm!!\n");
qm_rtc_set_alarm(RTC_0, (RTC[RTC_0].rtc_ccvr + ALARM));
rtc_fired++;
}
-----------------------------------------------------------------------
/**
* RTC configuration type.
*/
typedef struct {
uint32_t init_val; /**< Initial value in RTC clocks. */
bool alarm_en; /**< Alarm enable. */
uint32_t alarm_val; /**< Alarm value in RTC clocks. */
/**
* User callback.
*
* #param[in] data User defined data.
*/
void (*callback)(void *data);
void *callback_data; /**< Callback user data. */
} rtc_config_t;
name of the function is a pointer to the function
function will be called with arguments from rtc library, you are not invoking it (you cannot pass arguments here).
I guess that NULL assigned to custom_callback will not call custom method from library (default function or no function will be called), just assign NULL if you dont want to use custom callback.
usually library code looks like:
if(custom_callback)
{
custom_callback(some_parameters);
}
else
{
default_callback(some_parameters);
}
/*kernel calls attach the interrupt function handler to the hardware interrupt specified by intr(i.e irq) */
// InterruptAttach() : Attach an interrupt handler to an interrupt source
// interrupt source is handler1 for this example
void ConfigureISR(void) //void *ISR (void *arg)
{
/* the software must tell the OS that it wishes to associate the ISR with a particular source of //interrupts. On x86 platforms, there are generally 16 hardware Interrupt Request lines (IRQs) */
StartInterruptTime = GetTimeStamp(); //startTime of the interrupt
volatile int irq = 7; //0 : A clock that runs at the resolution set by ClockPeriod()
ThreadCtl (_NTO_TCTL_IO, NULL); // enables the hardware interrupt
id1 = InterruptAttach(irq, &ISR, NULL, 0, 0); // ISR is the interrupt service routine
//sleep(20);
}
int main ()
{
ConfigureISR();
return 1;
}
I created a interrupt handler on the server side to handle the interrupt from the client side. but the above code is breaking at the interrupt attach function call. could someone tell me why is it breaking ?? Is it the right way to handle interrupts in user application.
I am having some trouble, and I cannot tell if it's my understanding of the Atmel syntax, the Atmel Studio 6.0 or, the program itself.
I cannot seem to get the interrupt handler to receive a simple string then do something. I have success with just implimenting a single character turning an LED when USART receives one character it turns the LED on, then if it receives a different character it turns the LED off. By the way I have a design board that the program is having some trouble getting to the receive sub routine because the send code within the main is so large, so it was suggested to me to utilize interrupts to fix this.
By the way, I am trialing this program on an EVK1100 AVR32 board MCU:AT32UC3A0512-U, not sure if any of you have played with these before, but they're pretty great. Not sure I like Atmel syntax though.
Anyway, you can see I'm doing nothing in the main at the moment until I get the receive portion working.
I'm fairly new to interrupts in the Atmel world.
Any help would be much appreciated. I made just a few modifications to the built in ASF USART interrup "INTC" project example.
Thanks,
#include <string.h>
#include <avr32/io.h>
#include "compiler.h"
#include "board.h"
#include "print_funcs.h"
#include "intc.h"
#if defined (__GNUC__)
# if defined (__AVR32_AP7000__)
# include "pm_at32ap7000.h"
# else
# include "power_clocks_lib.h"
# endif
#elif defined (__ICCAVR32__) || defined (__AAVR32__)
# if defined (__AT32AP7000__)
# include "pm_at32ap7000.h"
# else
# include "power_clocks_lib.h"
# endif
#endif
#include "gpio.h"
#include "usart.h"
//#include "conf_example.h"
# define EXAMPLE_TARGET_PBACLK_FREQ_HZ FOSC0 // PBA clock target frequency, in Hz
#if BOARD == EVK1100
# define EXAMPLE_USART (&AVR32_USART1)
# define EXAMPLE_USART_RX_PIN AVR32_USART1_RXD_0_0_PIN
# define EXAMPLE_USART_RX_FUNCTION AVR32_USART1_RXD_0_0_FUNCTION
# define EXAMPLE_USART_TX_PIN AVR32_USART1_TXD_0_0_PIN
# define EXAMPLE_USART_TX_FUNCTION AVR32_USART1_TXD_0_0_FUNCTION
# define EXAMPLE_USART_CLOCK_MASK AVR32_USART1_CLK_PBA
# define EXAMPLE_PDCA_CLOCK_HSB AVR32_PDCA_CLK_HSB
# define EXAMPLE_PDCA_CLOCK_PB AVR32_PDCA_CLK_PBA
# define EXAMPLE_USART_IRQ AVR32_USART1_IRQ
# define EXAMPLE_USART_BAUDRATE 57600
#endif
/**
* \brief The USART interrupt handler.
*
* \note The `__attribute__((__interrupt__))' (under GNU GCC for AVR32) and
* `__interrupt' (under IAR Embedded Workbench for Atmel AVR32) C function
* attributes are used to manage the `rete' instruction.
*/
#if defined (__GNUC__)
__attribute__((__interrupt__))
#elif defined(__ICCAVR32__)
__interrupt
#endif
static void usart_int_handler(void)
{
static char Cmnd[30];
int index = 0;
int c;
usart_read_char(EXAMPLE_USART, &c);
Cmnd[index++] = c;
if (c = '\r')
{
Cmnd[index] = '\0';
usart_write_line(EXAMPLE_USART, Cmnd);
}
}
/**
* \brief The main function.
*
* It sets up the USART module on EXAMPLE_USART. The terminal settings are 57600
* 8N1.
* Then it sets up the interrupt handler and waits for a USART interrupt to
* trigger.
*/
int main(void)
{
static const gpio_map_t USART_GPIO_MAP =
{
{EXAMPLE_USART_RX_PIN, EXAMPLE_USART_RX_FUNCTION},
{EXAMPLE_USART_TX_PIN, EXAMPLE_USART_TX_FUNCTION}
};
// USART options.
static const usart_options_t USART_OPTIONS =
{
.baudrate = 57600,
.charlength = 8,
.paritytype = USART_NO_PARITY,
.stopbits = USART_1_STOPBIT,
.channelmode = USART_NORMAL_CHMODE
};
#if BOARD == EVK1100 || BOARD == EVK1101 || BOARD == UC3C_EK \
|| BOARD == EVK1104 || BOARD == EVK1105 || BOARD == STK600_RCUC3L0 \
|| BOARD == STK600_RCUC3D
/*
* Configure Osc0 in crystal mode (i.e. use of an external crystal
* source, with frequency FOSC0) with an appropriate startup time then
* switch the main clock source to Osc0.
*/
pcl_switch_to_osc(PCL_OSC0, FOSC0, OSC0_STARTUP);
#elif BOARD == STK1000
pm_reset();
#elif BOARD == UC3L_EK
/*
* Note: on the AT32UC3L-EK board, there is no crystal/external clock
* connected to the OSC0 pinout XIN0/XOUT0. We shall then program the
* DFLL and switch the main clock source to the DFLL.
*/
pcl_configure_clocks(&pcl_dfll_freq_param);
/*
* Note: since it is dynamically computing the appropriate field values
* of the configuration registers from the parameters structure, this
* function is not optimal in terms of code size. For a code size
* optimal solution, it is better to create a new function from
* pcl_configure_clocks_dfll0() and modify it to use preprocessor
* computation from pre-defined target frequencies.
*/
#end if
// Assign GPIO to USART.
gpio_enable_module(USART_GPIO_MAP,
sizeof(USART_GPIO_MAP) / sizeof(USART_GPIO_MAP[0]));
// Initialize USART in RS232 mode.
usart_init_rs232(EXAMPLE_USART, &USART_OPTIONS,
EXAMPLE_TARGET_PBACLK_FREQ_HZ);
print(EXAMPLE_USART, ".: Using interrupts with the USART :.\r\n\r\n");
// Disable all interrupts.
Disable_global_interrupt();
// Initialize interrupt vectors.
INTC_init_interrupts();
/*
* Register the USART interrupt handler to the interrupt controller.
* usart_int_handler is the interrupt handler to register.
* EXAMPLE_USART_IRQ is the IRQ of the interrupt handler to register.
* AVR32_INTC_INT0 is the interrupt priority level to assign to the
* group of this IRQ.
*/
INTC_register_interrupt(&usart_int_handler, EXAMPLE_USART_IRQ, AVR32_INTC_INT0);
// Enable USART Rx interrupt.
EXAMPLE_USART->ier = AVR32_USART_IER_RXRDY_MASK;
print(EXAMPLE_USART, "Type a character to use the interrupt handler."
"\r\nIt will show up on your screen.\r\n\r\n");
// Enable all interrupts.
Enable_global_interrupt();
/**
* We have nothing left to do in the main, so we may switch to a device
* sleep mode: we just need to be sure that the USART module will be
* still be active in the chosen sleep mode. The sleep mode to use is
* the FROZEN sleep mode: in this mode the PB clocks are still active
* (so the USART module which is on the Peripheral Bus will still be
* active while the CPU and HSB will be stopped).
* --
* Modules communicating with external circuits should normally be
* disabled before entering a sleep mode that will stop the module
* operation: this is not the case for the FROZEN sleep mode.
* --
* When the USART interrupt occurs, this will wake the CPU up which will
* then execute the interrupt handler code then come back to the
* while(1) loop below to execute the sleep instruction again.
*/
while(1)
{
/*
* If there is a chance that any PB write operations are
* incomplete, the CPU should perform a read operation from any
* register on the PB bus before executing the sleep
* instruction.
*/
AVR32_INTC.ipr[0]; // Dummy read
/*
* When the device wakes up due to an interrupt, once the
* interrupt has been serviced, go back into FROZEN sleep mode.
*/
}
}
Try to keep your interrupt handlers short. Interrupt handlers or ISRs are executed usually with interrupts disabled. Their goal is to get in, do something, and get out quickly. If you have other interrupts running (like a real-time clock for example), they are blocked out while you're in the ISR and that can cause problems such as lost interrupts.
The best thing to do here would be to declare the input buffer as a global static. Also define a static uchar_t have_line or similar, and in the ISR, save each character until either the buffer is full (an important check!), or you receive a CR (\r). Also check for newline (\n). Save a zero to null-terminate the buffer, and then set have_line = 1.
In your main loop, wait for have_line to be non-zero, and then process what's in the input buffer. Finally, set have_line back to zero.
That way, your ISR is brief and fairly robust.
In your sample code, does the function usart_write_line require interrupts to be enabled?
Oops! Sorry, I just noticed that your index isn't a static variable. That means it'll be initialized to zero each time the routine is called. Add a static declarator in front of int index and that should sort it out.
Hardware:
NXP M4 MKE14
Software:
MCUXpresso 10.1.1
Implementation of one software timer goes very well. When starting a second timer the microcontroller doesn't respond anymore. I get the error back "taskSCHEDULER_RUNNING". But if this not runs the system doesn't do anything.
I implemented the timers and one task like this:
/* Create the queue used by the queue send and queue receive tasks. */
xQueue = xQueueCreate(/* The number of items the queue can hold. */
mainQUEUE_LENGTH,
/* The size of each item the queue holds. */
sizeof(uint32_t));
xSensorTimer = xTimerCreate(/* A text name, purely to help
debugging. */
"SensorTimer",
/* The timer period, in this case
1000ms (1s). */
SENSOR_TIMER_PERIOD_MS,
/* This is a periodic timer, so
xAutoReload is set to pdTRUE. */
pdTRUE,
/* The ID is not used, so can be set
to anything. */
(void *)0,
/* The callback function */
vTimerCallback_SensorTimer);
if(xSensorTimer==NULL) {
for(;;); /*failure! */
}
xSUS_BUS_TIMEOUT_Timer = xTimerCreate(/* A text name, purely to help
debugging. */
"SUS_BUS_TIMEOUT_Timer",
/* The timer period, in this case
1000ms (1s). */
SUS_BUS_TIMEOUT_PERIOD_MS,
/* This is a periodic timer, so
xAutoReload is set to pdTRUE. */
pdFALSE,
/* The ID is not used, so can be set
to anything. */
(void *)1,
/* The callback function */
vTimerCallback_SUSBUSTIMEOUT);
if(xSUS_BUS_TIMEOUT_Timer==NULL) {
for(;;); /*failure! */
}
xTaskCreate(/* The function that implements the task. */
prvQueueModuleTask,
/* Text name for the task, just to help debugging. */
"Module",
/* The size (in words) of the stack that should be created
for the task. */
configMINIMAL_STACK_SIZE + 166,
/* A parameter that can be passed into the task. Not used
in this simple demo. */
NULL,
/* The priority to assign to the task. tskIDLE_PRIORITY
(which is 0) is the lowest priority. configMAX_PRIORITIES - 1
is the highest priority. */
mainQUEUE_MODULE_TASK_PRIORITY,
/* Used to obtain a handle to the created task. Not used in
this simple demo, so set to NULL. */
NULL);
/* Start the tasks and timers running. */
vTaskStartScheduler();
/* The program should never enter this while loop */
while(1)
{
;
}
}
static void vTimerCallback_SensorTimer (xTimerHandle pxTimer)
{
ReadTemperature();
ADCMeasurement();
}
static void vTimerCallback_SUSBUSTIMEOUT (xTimerHandle pxTimer)
{
//Reset the communication state back to the header state
}
Not sure what you mean by "got the error taskSCHEDULER_RUNNING back". When and how did you get that error?
With regards to the creating of timers; have you allocated enough heap for FreeRTOS (in your FreeRTOSConfig.h)?