1
0
mirror of https://github.com/elua/elua.git synced 2025-01-08 20:56:17 +08:00

Added documentation on interrupts:

Generic info/eLua interrupt handlers: using interrupt handlers from Lua and C
  Platform interface/CPU: updated CPU platform interface
  Platform interface/Timers: updated timers platform interface
  Generic modules/cpu: updated CPU module documentation
  Generic modules/tmr: updated timer module documentation
  eLua internals/Overview/Implementing interrupts: interrupt handlers implementation guide

Also minor bugfixes in the interrupt implementation.
This commit is contained in:
Bogdan Marinescu 2010-11-06 23:58:05 +00:00
parent 6302c1524e
commit 3612e87224
10 changed files with 529 additions and 440 deletions

View File

@ -111,7 +111,12 @@ local menu =
{ "R/W FAT FS in SD/MMC Cards", "fatfs.html" }
}
},
{ "eLua interrupt handlers", "inthandlers.html" },
{ "eLua interrupt handlers", "inthandlers.html",
{
{ "In Lua", "inthandlers.html" },
{ "In C", "inthandlers.html#cints" },
}
},
{ { "Building eLua", "Build de eLua" }, "building.html",
{
{ "Building eLua in Linux", "building_unix.html" },
@ -147,6 +152,11 @@ local menu =
{ { "Booting eLua", "O Boot de eLua" }, "arch_overview.html#boot" },
{ { "Platforms and ports", "Portabilização" }, "arch_overview.html#platforms" },
{ { "Adding a new port", "Portando eLua" }, "arch_newport.html" },
{ "Implementing interrupts", "arch_ints.html",
{
{ "Interrupt list", "arch_ints.html#intlist" }
}
}
}
},

View File

@ -14,41 +14,65 @@ data_en =
-- Functions
funcs =
{
{ sig = "void #platform_cpu_enable_interrupts#();",
desc = "Enable global interrupt on the CPU."
{ sig = "int #platform_cpu_set_global_interrupts#( int status );",
desc = "Set or clear the global interrupt flag of the CPU.",
args = "$status$ - $PLATFORM_CPU_ENABLE$ to set the global interrupt flag or $PLATFORM_CPU_DISABLE$ to clear the global interrupt flag.",
ret = "the previous value of the global interrupt flag (1 if set, 0 otherwise)."
},
{ sig = "void #platform_cpu_disable_interrupts#();",
desc = "Disable global interrupts on the CPU."
{ sig = "int #platform_cpu_get_global_interrupts#();",
desc = "Get the value of the global interrupt flag of the CPU.",
ret = "the value of the global interrupt flag (1 if set, 0 otherwise)."
},
{ sig = "u32 #platform_cpu_get_frequency#();",
desc = "Get the CPU frequency.",
ret = "the CPU $core$ frequency (in Hertz)."
},
}
}
data_pt =
{
-- Title
title = "eLua platform interface - CPU",
-- Menu name
menu_name = "CPU",
-- Overview
overview = "This part of the platform interface groups functions related to the CPU and its functional modules (interrupt controller, memory controller and others).",
-- Functions
funcs =
{ sig = "int #platform_cpu_set_interrupt#( elua_int_id id, elua_int_resnum resnum, int status );",
desc = "Enable or disable a specific CPU interrupt for a given resource ID.",
args =
{
{ sig = "void #platform_cpu_enable_interrupts#();",
desc = "Enable global interrupt on the CPU."
"$id$ - the interrupt ID, as defined in %platform_conf.h%.",
"$resnum$ - the resource ID.",
"$status$ - $PLATFORM_CPU_ENABLE to enable the interrupt or $PLATFORM_CPU_DISABLE$ to disable the interrupt.",
},
ret =
{
"$PLATFORM_INT_INVALID$ - invalid interrupt ID",
"$PLATFORM_INT_NOT_HANDLED$ - this interrupt cannot be enabled/disabled",
"$PLATFORM_INT_BAD_RESNUM$ - this resource ID can't be used to enable/disable the interrupt",
"the previous status of the interrupt (1 if enabled, 0 otherwise) if no error occured."
}
},
{ sig = "void #platform_cpu_disable_interrupts#();",
desc = "Disable global interrupts on the CPU."
{ sig = "int #platform_cpu_get_interrupt#( elua_int_id id, elua_int_resnum resnum );",
desc = "Get the interrupt enabled status of a specific CPU interrupt for a given resource ID.",
args =
{
"$id$ - the interrupt ID, as defined in %platform_conf.h%",
"$resnum$ - the resource ID"
},
ret =
{
"$PLATFORM_INT_INVALID$ - invalid interrupt ID",
"$PLATFORM_INT_NOT_HANDLED$ - this interrupt cannot be enabled/disabled",
"$PLATFORM_INT_BAD_RESNUM$ - this resource ID can't be used to enable/disable the interrupt",
"the status of the interrupt (1 if enabled, 0 otherwise) if no error occured."
}
},
{ sig = "int #platform_cpu_get_interrupt_flag#( elua_int_id id, elua_int_resnum resnum, int clear );",
desc = "Return the interrupt pending flag of a specific CPU interrupt or a given resource ID and optionally clear it.",
args =
{
"$id$ - the interrupt ID, as defined in %platform_conf.h%.",
"$resnum$ - the resource ID.",
"$clear$ - 1 to clear the interrupt pending flag if it is set, 0 otherwise."
},
ret =
{
"$PLATFORM_INT_INVALID$ - invalid interrupt ID.",
"$PLATFORM_INT_NOT_HANDLED$ - this interrupt's flag cannot be read.",
"$PLATFORM_INT_BAD_RESNUM$ - this resource ID can't be used to read this interrupt's flag.",
"the value of the interrupt pending flag if no error occured."
}
},
{ sig = "u32 #platform_cpu_get_frequency#();",
@ -57,3 +81,4 @@ data_pt =
},
}
}

View File

@ -138,6 +138,23 @@ enum
"$start$ - the second timer value",
},
ret = "the time difference (in microseconds)"
},
{ sig = "int #platform_timer_set_match_int#( unsigned id, u32 period_us, int type );",
desc = "Setup the timer match interrupt. Only available if interrupt support is enabed, check @inthandlers.html@here@ for details.",
args =
{
"$id$ - the timer ID",
"$period_us$ - the period (in microseconds) of the timer interrupt. Setting this to 0 disables the timer match interrupt.",
[[$type$ - $PLATFORM_TIMER_INT_ONESHOT$ for an interrupt that occurs only once after $period_us$ microseconds, or $PLATFORM_TIMER_INT_CYCLIC$ for an interrupt that occurs every
$period_us$ microseconds]]
},
ret =
{
"$PLATFORM_TIMER_INT_OK$ if the operation was successful.",
"$PLATFORM_TIMER_INT_TOO_SHORT$ if the specified period is too short.",
"$PLATFORM_TIMER_INT_INVALID_ID$ if the specified timer cannot handle this operation."
}
}
},
@ -179,194 +196,11 @@ enum
CPU time. The maximum value depends largely on the hardware and the desired behaviour of the virtual timers, but in practice values larger than 10 might visibly change the behaviour of your
system.</p>
<p>To $use$ a virtual timer, identify it with the constant $VTMR_FIRST_ID$ (defined in %inc/common.h%) plus an offset. For example, $VTMR_FIRST_ID+0$ (or simply
$VTMR_FIRST_ID$) is the ID of the first virtual timer in the system, and $VTMR_FIRST_ID+2$ is the ID of the third virtual timer in the system.
$VTMR_FIRST_ID$) is the ID of the first virtual timer in the system, and $VTMR_FIRST_ID+2$ is the ID of the third virtual timer in the system.</p>
<p>Virtual timers are capable of generating timer match interrupts just like regular timers, check @#platform_timer_set_match_int@here@ for details.
]]
}
}
}
data_pt =
{
-- Title
title = "eLua platform interface - timers",
-- Menu name
menu_name = "Timers",
-- Overview
overview = [[This part of the platform interface groups functions related to the timers of the MCU. It also makes provisions for using $virtual timers$ on any platform, see @#virtual@this section@
for details. Keep in mind that in the following paragraphs a $timer id$ can reffer to both a hardware timer or a virtual timer.]],
-- Data structures, constants and types
structures =
{
{ text = "typedef u32 timer_data_type;",
name = "Timer data type",
desc = "This defines the data type used to specify delays and time intervals (which are always specifide in $microseconds$)."
},
{ text = [[// Timer operations
enum
{
PLATFORM_TIMER_OP_START,
PLATFORM_TIMER_OP_READ,
PLATFORM_TIMER_OP_SET_CLOCK,
PLATFORM_TIMER_OP_GET_CLOCK,
PLATFORM_TIMER_OP_GET_MAX_DELAY,
PLATFORM_TIMER_OP_GET_MIN_DELAY
};]],
name = "Timer operations",
desc = "This enum lists all the operations that can be executed on a given timer."
}
},
-- Functions
funcs =
{
{ sig = "int #platform_timer_exists#( unsigned id );",
desc = [[Checks if the platform has the timer specified as argument. Implemented in %src/common.c%, it uses the $NUM_TIMER$ macro that must be defined in the
platform's $platform_conf.h$ file (see @arch_overview.html#platforms@here@ for details) and the virtual timer configuration (@#virtual@here@ for details). For example:</p>
~#define NUM_TIMER 2 $// The platform has 2 hardware timers$~<p>]],
args = "$id$ - the timer ID",
ret = "1 if the timer exists, 0 otherwise"
},
{ sig = "void #platform_timer_delay#( unsigned id, u32 delay_us );",
desc = [[Waits on a timer, then returns. This function is "split" in two parts: a platform-independent part implemented in %src/common.c% (that
handles virtual timers) and a platform-dependent part that must be implemented by each platform in a function named @#platform_s_timer_delay@platform_s_timer_delay@. This function handles both
hardware timer IDs and virtual timer IDs.<br>
<a name="limitations" /><span class="warning">IMPORTANT NOTE</span>: the real delay after executing this functions depends on a number of variables, most notably the base clock of the timer
and the size of the timer counter register (32 bits on some platforms, 16 bits on most platforms, other values are less common). To ensure that the delay you're requesting is achievable, use
@#platform_timer_op@platform_timer_op@ with $PLATFORM_TIMER_OP_GET_MAX_DELAY$ and $PLATFORM_TIMER_OP_GET_MIN_DELAY$ to obtain the maximum and the minimum
achievable wait times on your timer, respectively. Even if your delay is within these limits, the $precision$ of this function still varies a lot, mainly as a function of
the timer base clock.]],
args =
{
"$id$ - the timer ID",
"$delay_us$ - the delay time (in microseconds)"
}
},
{ sig = "void #platform_s_timer_delay#( unsigned id, u32 delay_us );",
desc = [[This function is identical in functionality to @#platform_timer_delay@platform_timer_delay@, but this is the function that must actually be implemented by a platform port,
and it must never handle virtual timer IDs, only hardware timer IDs. It has the same @#limitations@limitations@ as @#platform_timer_delay@platform_timer_delay@.]],
args =
{
"$id$ - the timer ID",
"$delay_us$ - the delay time (in microseconds)"
}
},
{ sig = "u32 #platform_timer_op#( unsigned id, int op, u32 data );",
desc = [[Executes an operation on a timer. This function is "split" in two parts: a platform-independent part implemented in %src/common.c% (that handles virtual timers) and a
platform-dependent part that must be implemented by each platform in a function named @#platform_s_timer_op@platform_s_timer_op@. This function handles both hardware timer IDs and virtual
timer IDs.]],
args =
{
"$id$ - the timer ID",
[[$op$ - the operation. $op$ can take any value from the @#timer_operations@this enum@, as follows:
<ul>
<li>$PLATFORM_TIMER_OP_START$: start the specified timer by setting its counter register to a predefined value.</li>
<li>$PLATFORM_TIMER_OP_READ$: get the value of the specified timer's counter register.</li>
<li>$PLATFORM_TIMER_SET_CLOCK$: set the clock of the specified timer to $data$ (in hertz). You can never set the clock of a virtual timer, which is set at compile time.</li>
<li>$PLATFORM_TIMER_GET_CLOCK$: get the clock of the specified timer.</li>
<li>$PLATFORM_TIMER_OP_GET_MAX_DELAY$: get the maximum achievable timeout on the specified timer (in us).</li>
<li>$PLATFORM_TIMER_OP_GET_MIN_DELAY$: get the minimum achievable timeout on the specified timer (in us).</li>
</ul>]],
"$data$ - used to specify the timer clock value when $op = PLATFORM_TIMER_SET_CLOCK$, ignored otherwise",
},
ret =
{
"the predefined value used when starting the clock if $op = PLATFORM_TIMER_OP_START$",
"the timer's counter register if $op = PLATFORM_TIMER_OP_READ$",
"the actual clock set on the timer, which might be different than the request clock depending on the hardware if $op = PLATFORM_TIMER_SET_CLOCK$",
"the timer clock if $op = PLATFORM_TIMER_GET_CLOCK$",
"the maximum achievable delay (in microseconds) if $op = PLATFORM_TIMER_OP_GET_MAX_DELAY$",
"the minimum achievable delay (in microseconds) if $op = PLATFORM_TIMER_OP_GET_MIN_DELAY$"
}
},
{ sig = "u32 #platform_s_timer_op#( unsigned id, int op, u32 data );",
desc = [[This function is identical in functionality to @#platform_timer_op@platform_timer_op@, but this is the function that must actually be implemented by a platform port, and it must
never handle virtual timer IDs, only hardware timer IDs.]],
args =
{
"$id$ - the timer ID",
[[$op$ - the operation. $op$ can take any value from the @#opval@this enum@, as follows:
<ul>
<li>$PLATFORM_TIMER_OP_START$: start the specified timer by setting its counter register to a predefined value.</li>
<li>$PLATFORM_TIMER_OP_READ$: get the value of the specified timer's counter register.</li>
<li>$PLATFORM_TIMER_SET_CLOCK$: set the clock of the specified timer to $data$ (in hertz). You can never set the clock of a virtual timer, which is set at compile time.</li>
<li>$PLATFORM_TIMER_GET_CLOCK$: get the clock of the specified timer.</li>
<li>$PLATFORM_TIMER_OP_GET_MAX_DELAY$: get the maximum achievable timeout on the specified timer (in us).</li>
<li>$PLATFORM_TIMER_OP_GET_MIN_DELAY$: get the minimum achievable timeout on the specified timer (in us).</li>
</ul>]],
"$data$ - used to specify the timer clock value when $op = PLATFORM_TIMER_SET_CLOCK$, ignored otherwise",
},
ret =
{
"the predefined value used when starting the clock if $op = PLATFORM_TIMER_OP_START$",
"the timer's counter register if $op = PLATFORM_TIMER_OP_READ$",
"the actual clock set on the timer, which might be different than the request clock depending on the hardware if $op = PLATFORM_TIMER_SET_CLOCK$",
"the timer clock if $op = PLATFORM_TIMER_GET_CLOCK$",
"the maximum achievable delay (in microseconds) if $op = PLATFORM_TIMER_OP_GET_MAX_DELAY$",
"the minimum achievable delay (in microseconds) if $op = PLATFORM_TIMER_OP_GET_MIN_DELAY$"
}
},
{ sig = "u32 #platform_timer_get_diff_us#( unsigned id, timer_data_type end, timer_data_type start );",
desc = [[Return the time difference (in us) betweeen two timer values. This function is generic for all platforms, thus it is implemented in %src/common.c%.]],
args =
{
"$id$ - the timer ID",
"$end$ - the first timer value",
"$start$ - the second timer value",
},
ret = "the time difference (in microseconds)"
}
},
auxdata =
{
{ title = "Virtual timers",
desc =
[[$Virtual timers$ were added to eLua to overcome some limitations:</p>
<ul>
<li>there are generally few hardware timers available, some of which might be dedicated (thus not usable directly by eLua).</li>
<li>many times it is difficult to share a hardware timer between different parts of an application because of conflicting requirements. Generally it's not possible to have timers that can
achieve long delays and high accuracy at the same time (this is especially true for systems that have 16 bit or even smaller timers).</li>
</ul>
<p>In this respect, $virtual timers$ are a set of timers that share a single hardware timer. It is possible, in this way, to have a hardware timer that can implement 4, 8 or more hardware
timers. There are a few drawbacks to this approach:</p>
<ul>
<li>the hardware timer used to implement the virtual timers must generally be dedicated. In fact in cat be still used in "read only mode", which means that the only operations that can
be executed on it are $PLATFORM_TIMER_OP_READ$, $PLATFORM_TIMER_GET_CLOCK$, $PLATFORM_TIMER_OP_GET_MAX_DELAY$ and $PLATFORM_TIMER_OP_GET_MIN_DELAY$. However,
since the "read only mode" is not enforced by the code, it is advisable to treat this timer as a dedicated resource and thus make it invisible to eLua by not associating it with
an ID.</li>
<li>the number of virtual timers and their base frequency are fixed at compile time.</li>
<li>virtual timers are generally used for large delays with low accuracy, since their base frequency should be fairly low (see below).</li>
</ul>
<p>To $enable$ virtual timers:</p>
<ol>
<li>edit $platform_conf.h$ (see @arch_overview.html#platforms@here@ for details) and set $VTMR_NUM_TIMERS$ to the number of desired virtual timers and
$VTMR_FREQ_HZ$ to the base frequency of the virtual timers (in hertz). For example:
~#define VTMR_NUM_TIMERS 4 // we need 4 virtual timers
#define VTMR_FREQ_HZ 4 // the base clock for the virtual timers is 4Hz~</li>
<li>in your platform port setup a hardware timer to fire an interrupt at $VTMR_FREQ_HZ$ and call the $cmn_virtual_timer_cb$ function (defined in %src/common.c%) in the
timer interrupt handler. For example, if the the interrupt handler is called $timer_int_handler$, do this:
~void timer_int_handler( void )
{
// add code to clear the timer interrupt flag here if needed
cmn_virtual_timer_cb();
}~</li>
</ol>
<p>Note that because of step 2 above you are limited by practical constraints on the value of $VTMR_FREQ_HZ$. If set too high, the timer interrupt will fire too often, thus taking too much
CPU time. The maximum value depends largely on the hardware and the desired behaviour of the virtual timers, but in practice values larger than 10 might visibly change the behaviour of your
system.</p>
<p>To $use$ a virtual timer, identify it with the constant $VTMR_FIRST_ID$ (defined in %inc/common.h%) plus an offset. For example, $VTMR_FIRST_ID+0$ (or simply
$VTMR_FIRST_ID$) is the ID of the first virtual timer in the system, and $VTMR_FIRST_ID+2$ is the ID of the third virtual timer in the system.
]]
}
}
}

View File

@ -31,7 +31,8 @@ $_C$. For example, to get the constants listed above declare your $PLATFORM_CPU_
_C( INT_GPIOB ),\
.................
_C( INT_UDMA )~
<p>It's worth to note that adding more constants does not increas RAM usage, only Flash usage, so you can expose as much constants as you need without worrying about RAM consumption.]]
<p>It's worth to note that adding more constants does not increas RAM usage, only Flash usage, so you can expose as much constants as you need without worrying about RAM consumption.<br />
This mechanism is also used to expose interrupt IDs to the CPU module, check @inthandlers.html@here@ for an overview of eLua interrupt support.]]
},
},
@ -83,19 +84,70 @@ $_C$. For example, to get the constants listed above declare your $PLATFORM_CPU_
ret = "$data$ - the byte read from memory."
},
{ sig = "#cpu.cli#()",
desc = "Disable CPU interrupts."
{ sig = "#cpu.cli#( [id], [resnum1], [resnum2], ... [resnumn])",
desc = "Disables the global CPU interrupt flag if called without arguments, or a specific interrupt (and the interrupt's capability of triggering Lua interrupt handlers) for a list of resource IDs if called with arguments.",
args =
{
"$id$ - the interrupt ID. If specified, at least one resource ID must also be specified.",
"$resnum1$ - the first resource ID, required if $id$ is specified.",
"$resnum2 (optional)$ - the second resource ID.",
"$resnumn (optional)$ - the #n#-th resource ID."
}
},
{ sig = "#cpu.sei#()",
desc = "Enable CPU interrupts."
{ sig = "#cpu.hw_cli#( [id], [resnum1], [resnum2], ... [resnumn])",
desc = "Disables the global CPU interrupt flag if called without arguments, or a specific interrupt for a list of resource IDs if called with arguments. The interrupt is only disabled at hardware level, this function doesn't affect the interrupt's ability of triggering Lua interrupt handlers.",
args =
{
"$id$ - the interrupt ID. If specified, at least one resource ID must also be specified.",
"$resnum1$ - the first resource ID, required if $id$ is specified.",
"$resnum2 (optional)$ - the second resource ID.",
"$resnumn (optional)$ - the #n#-th resource ID."
}
},
{ sig = "#cpu.sei#( [id], [resnum1], [resnum2], ... [resnumn])",
desc = "Enables the global CPU interrupt flag if called without arguments, or a specific interrupt (and the interrupt's capability of triggering Lua interrupt handlers) for a list of resource IDs if called with arguments.",
args =
{
"$id$ - the interrupt ID. If specified, at least one resource ID must also be specified.",
"$resnum1$ - the first resource ID, required if $id$ is specified.",
"$resnum2 (optional)$ - the second resource ID.",
"$resnumn (optional)$ - the #n#-th resource ID."
}
},
{ sig = "#cpu.hw_sei#( [id], [resnum1], [resnum2], ... [resnumn])",
desc = "Enables the global CPU interrupt flag if called without arguments, or a specific interrupt for a list of resource IDs if called with arguments. The interrupt is only enabled at hardware level, this function doesn't affect the interrupt's ability of triggering Lua interrupt handlers.",
args =
{
"$id$ - the interrupt ID. If specified, at least one resource ID must also be specified.",
"$resnum1$ - the first resource ID, required if $id$ is specified.",
"$resnum2 (optional)$ - the second resource ID.",
"$resnumn (optional)$ - the #n#-th resource ID."
}
},
{ sig = "clock = #cpu.clock#()",
desc = "Get the CPU core frequency.",
ret = "$clock$ - the CPU clock (in Hertz)."
}
},
{ sig = "#cpu.set_int_handler#( handler )",
desc = "Sets the Lua interrupt handler to function *handler*. Only available if interrupt support is enabled, check @inthandlers.html@here@ for details.",
args = "$handler$ - the Lua interrupt handler function, or *nil* to disable the Lua interrupt handler feature."
},
{ sig = "#cpu.get_int_flag#( id, resnum, [clear] )",
desc = "Get the interrupt pending flag of an interrupt ID/resource ID combination, and optionally clear the pending flag. Only available if interrupt support is enabled, check @inthandlers.html@here@ for details.",
args =
{
"$id$ - the interrupt ID.",
"$resnum$ - the resource ID.",
"$clear (optional)$ - $true$ to clear the interrupt pending flag or $false$ to leave the interrupt pending flag untouched. Defaults to $true$ if not specified."
}
}
}
}
data_pt = data_en

View File

@ -85,10 +85,18 @@ $NOTE:$ this function does not work with virtual timers.]]
desc = "Get the timer clock (the clock used to increment the timer counter register).",
args = "$id$ - the timer ID.",
ret = "The timer clock (in Hz)."
},
{ sig = "#tmr.set_match_int#( id, period, type )",
desc = "Setup the timer match interrupt. Only available if interrupt support is enabled, check @inthandlers.html@here@ for details.",
args =
{
"$id$ - the timer ID.",
"$period$ - the interrupt period in microseconds. Setting this to 0 disabled the timer match interrupt.",
"$type$ - $tmr.INT_ONESHOT$ to generate a single interrupt after *period* microseconds, or $tmr.INT_CYCLIC$ to generate interrupts every $period$ microseconds."
}
}
}
}
data_pt = data_en

100
doc/en/arch_ints.txt Normal file
View File

@ -0,0 +1,100 @@
// $$HEADER$$
eLua interrupt support implementation
-------------------------------------
To add interrupt support for an eLua platform follow the steps below:
1. *Define your interrupts*
+
Your interrupt sources should be defined in link:arch_platform.html[platform_conf.h] with macros (don't use C enumerations). The first one should have the value *ELUA_INT_FIRST_ID*
(defined in _inc/elua_int.h_), the next one *ELUA_INT_FIRST_ID + 1* and so on. Also, there should be a definition for a macro called *INT_ELUA_LAST* that must be equal to the largest
interrupt source value. An example is given below:
+
------------------------------
#define INT_GPIO_POSEDGE ELUA_INT_FIRST_ID
#define INT_GPIO_NEGEDGE ( ELUA_INT_FIRST_ID + 1 )
#define INT_TMR_MATCH ( ELUA_INT_FIRST_ID + 2 )
#define INT_ELUA_LAST INT_TMR_MATCH
------------------------------
+
Note that the interrupt names aren't random, they should follow a well defined pattern. Check <<intlist, here>> for details.
2. *Add them to the list of constants from the CPU module*
+
Check the documentation of the _mcpu module for details.
3. *Implement your support functions*
+
The actual implementation of the interrupt handlers is of course platform specific, so it can stay in the _platform.c_ file. However, since interrupt handlers might require quite a bit
of code, it is recommended to implement them in a separate file. The eLua convention is to use the _platform_int.c_ file for this purpose. For each interrupt defined in step 1 above, 3
functions need to be implemented:
+
--
* A function that enables or disables the interrupt and returns its previous state (enabled or disabled).
* A function that checks if the interrupt is enabled or disabled.
* A function that checks the interrupt pending flag and optionally clears it.
--
+
These functions are defined in _inc/elua_int.h_, which also defines an "int descriptor" type:
+
------------------------------
// Interrupt functions and descriptor
typedef int ( *elua_int_p_set_status )( elua_int_resnum resnum, int state );
typedef int ( *elua_int_p_get_status )( elua_int_resnum resnum );
typedef int ( *elua_int_p_get_flag )( elua_int_resnum resnum, int clear );
typedef struct
{
elua_int_p_set_status int_set_status;
elua_int_p_get_status int_get_status;
elua_int_p_get_flag int_get_flag;
} elua_int_descriptor;
------------------------------
+
_platform_int.c_ must have an array of *elua_int_descriptor* types named *elua_int_table* (remember to make it _const_ to save RAM). The elements of this array must be in the same
order as the interrupt sources. The interrupt table for the example from step 1 above might look like this:
+
------------------------------
const elua_int_descriptor elua_int_table[ INT_ELUA_LAST ] =
{
{ int_gpio_posedge_set_status, int_gpio_posedge_get_status, int_gpio_posedge_get_flag },
{ int_gpio_negedge_set_status, int_gpio_negedge_get_status, int_gpio_negedge_get_flag },
{ int_tmr_match_set_status, int_tmr_match_get_status, int_tmr_match_get_flag }
};
------------------------------
4. *Implement the init function*
+
_platform_int.c_ should implement a function named *platform_int_init* (defined in _inc/platform.h_) that must initialize all the required hardware and the internal data structures of
the interrupt subsystem. This function should be called from *platform_init*.
That's it. If you followed all these steps correctly, your platform should be fully able to support interrupt handlers (as described link:inthandlers.html[here]). Check the *lpc24xx*
platform implementation (_src/platform/lpc24xx_) for a full example.
[[intlist]]
Interrupt list and naming conventions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To ensure maximum portability and correct system behaviour, interrupt names (as defined in _platform_conf.h_) *must* follow a well-defined naming pattern. Please note that this isn't
merely a convention, many times the names must be properly chosen for the system to work properly. For example, the timer interrupt match will never happen on virtual timers if the
timer interrupt match name isn't *INT_TMR_MATCH* (see link:arch_platform_timers.html[here] for more details on how to use the timer match interrupt).
The naming rule is that the interrupt name must have the format *INT_<peripheral>_<type>_*, where:
* *peripheral* is a symbolic name of the peripheral to which the interrupt applies.
* *type* is a symbolic name of the interrupt type.
This restriction applies only to interrupt *names*. The value associated with the interrupt name (as defined in _platform_conf.h_) can vary from platform to platform, as long as
it follows the rules outlined in step 1 above.
The table below lists all the valid interrupt names currently known to eLua. If you add a new interrupt don't forget to update the table below.
[width="70%", cols="<2s,<5", options="header"]
|===================================================================
^| Name ^| Meaning
| INT_GPIO_POSEDGE | Interrupt on a positive edge on a GPIO pin
| INT_GPIO_NEGEDGE | Interrupt on a negative edge on a GPIO pin
| INT_TMR_MATCH | Interrupt on timer match
|===================================================================
// $$FOOTER$$

View File

@ -69,5 +69,8 @@ _STM3210E-EVAL=http://www.st.com/mcu/contentid-100-110-STM3210E_EVAL.html[STM321
_ET-STM32Stamp=http://www.futurlec.com/ET-STM32_Stamp.shtml[ET-STM32 Stamp]
_mbed=http://mbed.org/[mbed]
# Modules
_mcpu=link:refman_gen_cpu.html[CPU]
# Miscellaneous
_br=<br />

View File

@ -1,203 +0,0 @@
$$HEADER$$
<h3>eLua interrupt handlers</h3>
<p>Starting with version 0.8, <b>eLua</b> supports interrupt handlers written in Lua. Once an interrupt handler is set in the Lua code, it will be called each time a supported
interrupt is generated. A <b>supported interrupt</b> is any interrupt that is handled by the platform C code (see <a href="#cints">here</a> for more details). </p>
<p><b><font color="red">IMPORTANT:</font></b> before learning how to use interrupt handlers in Lua, please keep in mind that Lua interrupt handlers don't work the same way as
regular (C) interrupt handlers (although it might appear so). As Lua doesn't have direct suport for interrupts, they have to be emulated. <b>eLua</b> emulates them using a
queue that is populated with interrupt data by the C support code. As long as the queue is not empty, a Lua hook is set to run every 2 Lua bytecode instructions,
and the hook function is the Lua interrupt handler. After all the interrupts are handled and the queue is emptied, the hook is automatically disabled. Consequently: </p>
<ul>
<li>When the interrupt queue is full (a situation that might appear when interrupts are added to the queue faster than the Lua code can handle them) subsequent interrupts are
ignored (not added to the queue) and an error message is printed on the <b>eLua</b> console device. The interrupt queue size can be configured at build time, as explained
<a href="[TODO]">here</a>. Even if the interrupt queue is large, one most remember that Lua code is significantly slower than C code, thus not all C interrupts make
suitable candidates for Lua interrupt handlers. For example, a serial interrupt that is generated each time a char is received at 115200baud might be too fast for Lua
(this is largely dependent on the platform). On the other hand, a GPIO interrupt-on-change on a GPIO line connected with a matrix keyboard is a very good candidate for
a Lua handler. Experimenting with different interrupt types is the best way to find the interrupts that work well with Lua.</li>
<li>A more subtle point is that the Lua virtual machine must <b>run</b> for the interrupt handlers to work. A simple analogy is that a CPU must have a running clock in order
to function properly (and in order to take care of the hardware interrupts). If the clock is stopped, the CPU doesn't run and the interrupt handlers aren't called anymore,
although the occurence of the interrupt might be recorded inside the CPU. This is the exact same situation with Lua: if the virtual machine doesn't run, the interrupts
are still recorded in the interrupt queue, but the Lua handler won't be called until the virtual machine runs again. In this case though, the "clock" of the Lua VM is a
C function that is executed for every VM instruction. If this function blocks for some reason, the VM instructions are not executed anymore. It's not hard to make
this function block; for example, it blocks everytime the Lua code waits for some user input at the console, or when a tmr.delay is executed, or when uart.read is called
with an infinite or very large timeout; in general, any function from a Lua library that doesn't return immediately (or after a short ammount of time) will block the VM.
Care must be taken to avoid such operations as much as possible, otherwise the interrupt support code won't run properly.</li>
<li>There is a single interrupt handler in Lua, as opposed to the many hardware interrupt handlers usually found on the <b>eLua</b> targets. It is however easy to differentiate
between different interrupt sources, as will be explained in the next paragraph.</li>
<li>Lua interrupt handlers are never reentrant.</li>
</ul>
<p>While this might seem restrictive, Lua interrupt handlers work quite well in practical situations. As an added bonus, since they are implemented by C support code, there's nothing
preventing <b>eLua</b> from implementing "custom interrupts" (software generated interrupts that don't correspond to a hardware interrupt on the CPU), such as serial interrupt on
char match (generate an interrupt when a certain char is received on the serial port, for example a newline), timer interrupts for <a href="[TODO]">virtual timers</a>, TCP/UDP data
packet received interrupt, and many others.</p>
<h3>Using interrupt handlers in Lua</h3>
<p>Setting up interrupt handlers is a straightforward process, most of the required functionality is provided by the <a href="refman_gen_cpu.html">CPU module</a>:</p>
<ul>
<li>use <i>cpu.set_int_handler</i> to set the interrupt handler function (call with <b>nil</b> to disable interrupt handling in Lua code.</li>
<li>use <i>cpu.sei( int_id, resnum1, [resnum2], ..., [resnumn])</i> and <i>cpu.cli( int_id, resnum1, [resnum2], ..., [resnumn])</i> to enable/disable specific CPU interrupts
that will trigger the interrupt handler (use <i>cpu.sei()</i> and <i>cpu.cli</i> (without parameters) to enable/disable global interrupts on the CPU).</li>
</ul>
<p>The interrupt handler receives two arguments: the interrupt type (all the interrupt types are mapped to constants from the <a href="refman_gen_cpu.html">CPU module</a>) and a
resource ID that specifies the target resource for the interrupt. This depends on the interrupt type; it can be a timer ID for a timer overflow interrupt, a GPIO port/pin combination
for a GPIO interrupt on pin change, a SPI interface ID for a SPI data available interrupt, and so on.</p>
<p>An example that uses the above concepts and knows how to handle two different interrupt types is presented below:</p>
<pre><code>local vtmrid = tmr.VIRT0
local to = 1500000
-- This is the interrupt handler
<b>local function handler( id, resnum )</b>
print( string.format( "Got interrupt with id %d and resnum %d", id, resnum ) )
-- Identify interrupt
if id == cpu.INT_GPIO_POSEDGE or id == cpu.INT_GPIO_NEGEDGE then
local port, pin = pio.decode( resnum )
print( string.format( "Port is %d, pin is %d", port, pin ) )
elseif id == cpu.INT_TMR_MATCH then
-- Timer interrupt on match is one shot, need to rearm to get a periodic timer interrupt
print "Timer interrupt! Rearming ... "
tmr.setinttimeout( vtmrid, to )
end
end
<b>cpu.set_int_handler( handler )</b> -- set interrupt handler
<b>cpu.sei( cpu.INT_GPIO_NEGEDGE, pio.P0_0 )</b> -- enable GPIO interrupt on change (negative edge) for pin 0 of port 0
<b>cpu.sei( cpu.INT_TMR_MATCH, vtmrid )</b> -- enable timer match interrupt on virtual timer 0
tmr.setinttimeout( vtmrid, to ) -- set interrupt timeout ( for timer match interrupt) on virtual timer 0
local tmrid = 0
-- Enter an infinite loop that prints "Outside interrupt" every second
-- This output will be interleaved with the interrupt handler timeout:
-- the timer interrupt message will appear every 1.5 seconds
-- the GPIO message will appear each time pin 0 of port 0 changes state from 1 to 0
while true do
print "Outside interrupt"
for i = 1, 1000 do tmr.delay( tmrid, 1000 ) end
end
</code></pre>
<br />
<a name="cints" />
<h3>Support code for Lua interrupt handlers</h3>
<p><b><font color="red">NOTE:</font></b> this paragraph describes the support code that must be implemented in C to make the existence of Lua interrupt handlers possible. Consequently,
it's only relevant for <b>eLua</b> developers. If you're just using <b>eLua</b> as is, you can safely skip this paragraph.</p>
<p>Specific C code is required to make Lua interrupt handlers functional. An <b>eLua</b> platform that needs to implement Lua interrupt handlers must respect these rules:</p>
<ul>
<li>it must have a functional interrupt subsystem (the part of the code that knows how to handle CPU interrupts).</li>
<li>it must define the interrupts that will be accesible from Lua handlers with symbolic names, see <a href="refman_gen_cpu.html#structures">here</a> for details.</li>
<li>it must add code to enable and disable specific interrupts (in <i>platform_cpu_set_interrupt</i>) and also get the state of a specific interrupt (enabled/disabled) (in
<i>platform_cpu_get_interrupt</i>. See <a href="arch_platform.cpu.html">the CPU platform interface documentation</a> for details.</li>
<li>it must write the actual CPU interrupt handlers (in C/ASM) and link them to the <b>eLua</b> interrupt support code. At the very least, the interrupt handler should clear the
interrupt flag and call <i>elua_int_add</i> with two parameters: the interrupt ID and the resource ID.</li>
</ul>
<p>This is best illustrated with an example. The code below is a fragment from the lpc24xx backend and shows how the GPIO interrupt on change can be made available from Lua. First, the
interrupt is declared explicitly in <i>platform_conf.h</i>:</p>
<pre><code>// Interrupt list
enum
{
// Platform interrupts
<b> INT_GPIO_POSEDGE = ELUA_INT_FIRST_ID,
INT_GPIO_NEGEDGE</b>
};
#define PLATFORM_CPU_CONSTANTS\
_C( IO_PINSEL0 ),\
_C( IO_PINSEL1 ),\
_C( IO_PINSEL2 ),\
_C( IO_PINSEL3 ),\
_C( IO_PINSEL4 ),\
_C( IO_PINSEL5 ),\
_C( IO_PINSEL6 ),\
_C( IO_PINSEL7 ),\
_C( IO_PINSEL8 ),\
_C( IO_PINSEL9 ),\
_C( IO_PINSEL10 ),\
<b> _C( INT_GPIO_POSEDGE ,\
_C( INT_GPIO_NEGEDGE )</b>
</code></pre>
<p>Then code is added in <i>platform.c</i> to enable/disable these interrupts and get their status:</p>
<pre><code>static PREG const posedge_regs[] = { ( PREG )&amp;IO0_INT_EN_R, NULL, ( PREG )&amp;IO2_INT_EN_R };
static PREG const negedge_regs[] = { ( PREG )&amp;IO0_INT_EN_F, NULL, ( PREG )&amp;IO0_INT_EN_F };
// Helper: return the status of a specific interrupt (enabled/disabled)
static int platform_cpuh_get_int_status( elua_int_id id, elua_int_resnum resnum )
{
int port, pin;
if( id == INT_GPIO_POSEDGE || id == INT_GPIO_NEGEDGE )
{
port = PLATFORM_IO_GET_PORT( resnum );
pin = PLATFORM_IO_GET_PIN( resnum );
if( id == INT_GPIO_POSEDGE )
return *posedge_regs[ port ] &amp; ( 1 &lt;&lt; pin );
else
return *negedge_regs[ port ] &amp; ( 1 &lt;&lt; pin );
}
return 0;
}
<b>int platform_cpu_set_interrupt( elua_int_id id, elua_int_resnum resnum, int status )</b>
{
int crt_status = platform_cpuh_get_int_status( id, resnum );
int port, pin;
if( id == INT_GPIO_POSEDGE || id == INT_GPIO_NEGEDGE )
{
port = PLATFORM_IO_GET_PORT( resnum );
pin = PLATFORM_IO_GET_PIN( resnum );
if( id == INT_GPIO_POSEDGE )
{
if( status == PLATFORM_CPU_ENABLE )
*posedge_regs[ port ] |= 1 &lt;&lt; pin;
else
*posedge_regs[ port ] &amp;= ~( 1 &lt;&lt; pin );
}
else
{
if( status == PLATFORM_CPU_ENABLE )
*negedge_regs[ port ] |= 1 &lt;&lt; pin;
else
*negedge_regs[ port ] &amp;= ~( 1 &lt;&lt; pin );
}
}
return crt_status;
}
<b>int platform_cpu_get_interrupt( elua_int_id id, elua_int_resnum resnum )</b>
{
return platform_cpuh_get_int_status( id, resnum );
}
</code></pre>
<p>An interrupt handler must also be defined for this type of interrupt, and it must call <i>elua_add_int</i> to enqueue the interrupt:</p>
<pre><code>static void int_handler_eint3()
{
elua_int_id id = ELUA_INT_INVALID_INTERRUPT;
pio_code resnum = 0;
int pidx, pin;
EXTINT |= 1 &lt;&lt; 3; // clear interrupt
// Look for interrupt source
// In can only be GPIO0/GPIO2, as the EXT interrupts are not (yet) used
pidx = ( IO_INT_STAT &amp; 1 ) ? 0 : 1;
if( *posedge_status[ pidx ] )
{
id = INT_GPIO_POSEDGE;
pin = intlog2( *posedge_status[ pidx ] );
}
else
{
id = INT_GPIO_NEGEDGE;
pin = intlog2( *negedge_status[ pidx ] );
}
<b>// The resource corresponding to the interrupt is computed here, in this case it is a port/pin combination
resnum = PLATFORM_IO_ENCODE( pidx * 2, pin, PLATFORM_IO_ENC_PIN );
// Also, the interrupt source must be cleared in the handler
*intclr_regs[ pidx ] = 1 &lt;&lt; pin;</b>
// Queue interrupt
<b>elua_int_add( id, resnum );</b>
VICVectAddr = 0; // ACK interrupt
}
</code></pre>
$$FOOTER$$

188
doc/en/inthandlers.txt Normal file
View File

@ -0,0 +1,188 @@
// $$HEADER$$
eLua interrupt handlers
-----------------------
Starting with version link:TODO[0.8], eLua supports interrupt handlers written in Lua. Once an interrupt handler is set in the Lua code, it will be called each time a supported
interrupt is generated. A *supported interrupt* is any interrupt that is handled by the platform C code (see link:#cints[here] for more details).
[red]*IMPORTANT*: before learning how to use interrupt handlers in Lua, please keep in mind that Lua interrupt handlers don't work the same way as
regular \(C) interrupt handlers. As Lua doesn't have direct suport for interrupts, they have to be emulated. eLua emulates them using a queue that is populated with
interrupt data by the C support code. As long as the queue is not empty, a Lua hook is set to run every 2 Lua bytecode instructions. This hook function is the Lua interrupt
handler. After all the interrupts are handled and the queue is emptied, the hook is automatically disabled. Consequently:
* When the interrupt queue is full (a situation that might appear when interrupts are added to the queue faster than the Lua code can handle them) subsequent interrupts are
ignored (not added to the queue) and an error message is printed on the eLua console device. The interrupt queue size can be configured at build time, as explained
link:TODO[here]. Even if the interrupt queue is large, one most remember that Lua code is significantly slower than C code, thus not all C interrupts make
suitable candidates for Lua interrupt handlers. For example, a serial interrupt that is generated each time a char is received at 115200 baud might be too fast for Lua
(this is largely dependent on the platform). On the other hand, a GPIO interrupt-on-change on a GPIO line connected with a matrix keyboard is a very good candidate for
a Lua handler. Experimenting with different interrupt types is the best way to find the interrupts that work well with Lua.
* A more subtle point is that the Lua virtual machine must *run* for the interrupt handlers to work. A simple analogy is that a CPU must have a running clock in order
to function properly (and in order to take care of the hardware interrupts). If the clock is stopped, the CPU doesn't run and the interrupt handlers aren't called anymore,
although the occurence of the interrupt might be recorded inside the CPU. This is the exact same situation with Lua: if the virtual machine doesn't run, the interrupts
are still recorded in the interrupt queue, but the Lua handler won't be called until the virtual machine runs again. In this case though, the "clock" of the Lua VM is a
C function that is executed for every VM instruction. If this function blocks for some reason, the VM instructions are not executed anymore. It's not hard to make
this function block; for example, it blocks everytime the Lua code waits for some user input at the console, or when a TODO tmr.delay is executed, or when TODO uart.read is called
with an infinite or very large timeout; in general, any function from a Lua library that doesn't return immediately (or after a short ammount of time) will block the VM.
Care must be taken to avoid such operations as much as possible, otherwise the interrupt support code won't run properly.
* There is a single interrupt handler in Lua, as opposed to the many hardware interrupt handlers usually found on the eLua targets. It is however easy to differentiate
between different interrupt sources, as will be explained in the next paragraph.
* Lua interrupt handlers are never reentrant.
While this might seem restrictive, Lua interrupt handlers work quite well in practical situations. As an added bonus, since they are implemented by C support code, there's nothing
preventing eLua from implementing "custom interrupts" (software generated interrupts that don't correspond to a hardware interrupt on the CPU), such as serial interrupt on
char match (generate an interrupt when a certain char is received on the serial port, for example a newline), timer interrupts for link:TODO[virtual timers], TCP/UDP data
packet received interrupt, and many others.
Using interrupt handlers in Lua
--------------------------------
To enable Lua interrupt handler, define *BUILD_LUA_INT_HANDLERS* in _platform_conf.h_. Setting up interrupt handlers is a straightforward process, most of the required
functionality is provided by the _mcpu module:
* use _cpu.set_int_handler_ to set the interrupt handler function (call with *nil* to disable interrupt handling in Lua code).
* use _cpu.sei( int_id, resnum1, [resnum2], ..., [resnumn])_ and _cpu.cli( int_id, resnum1, [resnum2], ..., [resnumn])_ to enable/disable specific CPU interrupts
that will trigger the interrupt handler. You can also use _cpu.sei()_ and _cpu.cli_ (without parameters) to enable/disable global interrupts on the CPU, although this
is not recommended.
The interrupt handler receives two arguments: the interrupt *type* (all the interrupt types are mapped to constants from the _mcpu module) and a
*resource ID( that specifies the target resource for the interrupt. This depends on the interrupt type; it can be a timer ID for a timer overflow interrupt, a GPIO port/pin combination
for a GPIO interrupt on pin change, a SPI interface ID for a SPI data available interrupt, and so on.
An example that uses the above concepts and knows how to handle two different interrupt types is presented below:
[subs="quotes"]
-------------------------------
local vtmrid = tmr.VIRT0
local to = 1500000
-- This is the interrupt handler
[bblue]*local function handler( id, resnum )*
print( string.format( "Got interrupt with id %d and resnum %d", id, resnum ) )
-- Identify interrupt
if id == [bblue]*cpu.INT_GPIO_POSEDGE* or id == [bblue]*cpu.INT_GPIO_NEGEDGE* then
local port, pin = pio.decode( resnum )
print( string.format( "Port is %d, pin is %d", port, pin ) )
elseif [bblue]*id == cpu.INT_TMR_MATCH* then
-- Timer interrupt on match is one shot, need to rearm to get a periodic timer interrupt
print "Timer interrupt!"
end
end
[bblue]*cpu.set_int_handler( handler )* -- set interrupt handler
[bblue]*tmr.set_match_int( vtmrid, to, tmr.INT_CYCLIC )* -- enable periodic timer interrupt for virtual timer 0
[bblue]*cpu.sei( cpu.INT_GPIO_NEGEDGE, pio.P0_0 )* -- enable GPIO interrupt on change (negative edge) for pin 0 of port 0
[bblue]*cpu.sei( cpu.INT_TMR_MATCH, vtmrid )* -- enable timer match interrupt on virtual timer 0
local tmrid = 0
-- Enter an infinite loop that prints "Outside interrupt" every second
-- This output will be interleaved with the interrupt handler timeout:
-- the timer interrupt message will appear every 1.5 seconds
-- the GPIO message will appear each time pin 0 of port 0 changes state from 1 to 0
while true do
print "Outside interrupt"
for i = 1, 1000 do tmr.delay( tmrid, 1000 ) end
end
-------------------------------
This is the most common use case for Lua interrupts, but it's not the only one. Another way to use interrupts from eLua uses polling instead of interrupt handlers: directly
check the interrupt flags and execute a certain action when one of them becomes set. For this, use the _cpu.get_int_flag( id, resnum, [clear] )_ function from the _mcpu module,
which returns the specified interrupt's status for resource *resnum*. *clear* is an optional boolean parameter, specifying if the interrupt flag should be cleared if it is set.
It defaults to *true*, and in most cases it shouldn't be changed. Using this feature, it becomes easy to wait for one or more interrupt flag(s) to be set. To use interrupt
polling:
* Enable/disable interrupts to be polled with _cpu.hw_sei_/_cpu.hw_cli_ instead of _cpu.sei_/_cpu.cli_. These functions enable/disable interrupts only in hardware,
as opposed to _cpu.sei_/_cpu.cli_ that also set/clear an internal flag which makes the interrupt able to trigger a Lua handler.
* Use _cpu.get_int_flag_ to get the interrupt flag.
The *int_select* function below is a possible implementation of a function that gets an array of interrupts and returns the first one that gets active:
[subs="quotes"]
------------------------------
function int_select( int_table )
while true do
for i = 1, #int_table do
local t = int_table[ i ]
if [bblue]*cpu.get_int_flag[ t[ 1 ], t[ 2 ] )* then
return t[ 1 ], t[ 2 ]
end
end
end
end
[bblue]*cpu.hw_sei( cpu.INT_GPIO_NEGEDGE, pio.P0_0 )*
[bblue]*cpu.hw_sei( cpu.INT_TMR_MATCH, tmr.VIRT0 )*
local ints = { { cpu.INT_GPIO_NEGEDGE, pio.P0_0 }, { cpu.INT_TMR_MATCH, tmr.VIRT0 } }
-- int_select will wait for either INT_GPIO_NEGEDGE or INT_TMR_MATCH to become active
print( int_select( ints ) )
------------------------------
Note that the two mechanisms (interrupt handlers and polling) can be used in parallel as long as an interrupt is not set with both _cpu.hw_sei_ and _cpu.sei_, in which case the bevahiour
is unpredictable. This is why it makes sense to write the *int_select* function above in Lua instead of C: it keeps the Lua VM running, so Lua interrupt handlers can be executed.
[[cints]]
Interrupt handlers in C
-----------------------
The interrupt subsystem has also a basic C API that can be used to implement portable eLua components and modules. It is enabled by defining *BUILD_C_INT_HANDLERS* in _platform_conf.h_.
It is defined in _inc/elua_int.h_ and has 2 functions:
elua_int_c_handler elua_int_set_c_handler( elua_int_id inttype, elua_int_c_handler phandler )::
Sets the interrupt handler for interrupt *inttype* to *phandler* and returns the previous interrupt handler for interrupt *inttype*.
elua_int_c_handler elua_int_get_c_handler( elua_int_id inttype )::
Returns the interrupt handler for interrupt *inttype*
*elua_int_c_handler* is a function that doesn't return anything and receives a single parameter of type *elua_int_resnum* to differentiate between the sources (GPIO pin, UART id, timer id
and so on) that can trigger the interrupt *inttype*. Note that the C interrupt model defines an interrupt handler per interrupt type, as opposed to the Lua interrupt model that defines
a single interrupt handler for all interrupt types.
To work with interrupts from C code use these functions defined by the link:TODO[CPU platform interface]:
int platform_cpu_set_interrupt( elua_int_id id, elua_int_resnum resnum, int status )::
Enable (*status* = *PLATFORM_CPU_ENABLE*) or disable (*status* = *PLATFORM_CPU_DISABLE*) interrupt *id* for resource *resnum*.
int platform_cpu_get_interrupt( elua_int_id id, elua_int_resnum resnum )::
Returns 1 if interrupt *id* is enabled for resource *resnum*, 0 otherwise.
int platform_cpu_get_interrupt_flag( elua_int_id id, elua_int_resnum resnum, int clear )::
Get interrupt flag for interrupt *id* and resource *resnum*, clear interrupt flag if it is set and *clear* is 1, leave it untouched otherwise.
Since _elua_int_set_c_handler_ returns the previous handler, it is easy to chain the interrupt handlers from different system components. To ensure correct operation, every C module that
needs access to interrupt handlers should use this sequence:
[subs="quotes"]
------------------------------
#include "elua_int.h"
static elua_int_c_handler prev_handler;
static void int_handler( elua_int_resnum resnum );
void module_init()
{
int id = SOME_INT_ID;
platform_cpu_set_interrupt( id, some_resnum, PLATFORM_CPU_ENABLE );
prev_handler = elua_int_set_c_handler( id, int_handler );
}
static void int_handler( elua_int_resnum resnum )
{
// Note: prev_handler can also be called at the end of int_handler
if( prev_handler )
prev_handler( resnum );
// (Optional) Check resnum and return if the interrupt was fired by a different resource
if( resnum != some_resnum )
return;
// Actual interrupt handler code comes here
}
------------------------------
// $$FOOTER$$

View File

@ -9,7 +9,7 @@ body {
padding: 0;
}
pre {
div.sectionbody pre {
border: 1px dashed #fb2;
background-color: #fda;
font-family: Monotype.com,"Courier New",Courier,monospace;
@ -39,6 +39,7 @@ pre {
padding: 0;
}
.logo_elua {
margin-top: 10px;
margin-left: 10px;
@ -171,9 +172,9 @@ table.invisible td {
margin-left: 0;
}
.code {
div.sectionbody pre.code {
border: 0;
background-color: #FFF;
background-color: #FFFFFF;
font-family: "Courier New",Courier,monospace;
font-size: 90%;
overflow: auto;
@ -222,9 +223,10 @@ strong {
/*color: #083194;*/
}
tt {
/* BogdanM: commented out the color inside <tt> blocks */
/*tt {
color: navy;
}
}*/
/* BogdanM: commented out the next 5 rules */
/*h1, h2, h3, h4, h5, h6 {
@ -362,11 +364,12 @@ div.sidebarblock > div.content {
padding: 0.5em;
}
div.listingblock > div.content {
/* BogdanM: commented out the next defition */
/*div.content div.listingblock {
border: 1px solid silver;
background: #f4f4f4;
padding: 0.5em;
}
}*/
div.quoteblock, div.verseblock {
padding-left: 1.0em;
@ -520,6 +523,7 @@ td.hdlist1 {
vertical-align: top;
font-style: normal;
padding-right: 0.8em;
margin-left: 1em;
color: navy;
}
td.hdlist2 {
@ -604,7 +608,75 @@ div.toclevel4 {
font-size: 0.9em;
}
/******************************************************************************/
/* BogdanM: new rules for eLua docs */
span.orange {
color: rgb(255, 102, 0);
};
}
span.red {
color: rgb(255, 0, 0);
}
pre.mono, pre.func {
border: 1px dashed #fb2;
background-color: #fda;
font-family: Monotype.com,"Courier New",Courier,monospace;
font-size: 90%;
line-height: 125%;
margin-left: 1em;
overflow: auto;
padding: 4px;
}
pre.mono p, pre.func p {
margin: 0;
padding: 0;
}
div.openblock.right {
margin-left: 2em;
}
div.openblock.right p {
margin: 0;
padding: 0;
}
div.listingblock div.content pre {
/*border: 0;*/
/*background-color: #FFFFFF;*/
border: 1px solid silver;
background: #f4f4f4;
font-family: "Courier New",Courier,monospace;
font-size: 90%;
overflow: auto;
padding: 4px 4px 4px 1em;
/* margin: 0;
padding: 0;
border: 0;
background: transparent;
font-family: Monotype.com,"Courier New",Courier,monospace;
font-size: 100%;
overflow: auto;*/
}
div.listingblock div.content tt {
color: black;
font-size: 110%;
}
span.bblue {
color: rgb(0, 0, 255);
font-weight: bold;
}
.dlist dl {
margin-left: 2em;
}
dd {
display: block;
margin-left: 20px;
}