Folks, I am working on an embedded system in C that uses an I2C module. Other modules that use the I2C have the I2C module as a dependency, or "child system". They pass it message context structs and when the I2C module is ready it asynchronously sends or reads messages as the context instructs, running a callback from the context to let the overlying module know the work has completed, successfully or with error.
I would like the I2C module to be able to recover itself from errors i.e. reset external connected hardware, itself and then reinitialise the external devices. the former two of these three actions can be handled internal to the I2C module with no problem, however the I2C module doesn't know what overlying modules may want to use it and need to send data to external devices to configure them again after their earlier hardware reset.
The message contexts could have callbacks to reinitialisation functions, but the I2C module only holds a reference to one of these at a time, the one it is currently working on how would it know to call the reinitialise handle for the next time a different module (with reset hardware) wants to talk to it's external hardware? the overlying module doesn't know the I2C has been reset, and the I2C module doesn't know if the overlying module has performed its reinitialisation sequence or not, or even if it needs to.
Is there a common design pattern to ameliorate such a problem?
To illustrate differently:
Normal initialisation of I2C and external hardware sequence:
initExtIOExpander() {
context_t initialisationMsg = {/*...*/};
i2cSend(&initialisationMsg )
}
initExtADC() {
context_t initialisationMsg = {/*...*/};
i2cSend(&initialisationMsg )
}
i2cSend() {
// Start sending data using passed context,
}
interrupt i2c_isr() {
//If the message completed run the callback handle in the current context
// else do the next part of the message, etc.
}
main () {
//...
initI2c();
initExtIOExpander();
initExtADC();
//...
// Use external devices here.
//
}
Reset and reinitialisation sequence:
If the normal sequence from above has already happened but now the I2C ISR is altered to detects errors:
interrupt i2c_isr () {
//If there was an error reset external H/W, and own H/W
// but how to know to call initExtIOExpander() and initExtADC()??
// this module might know about the ADC if it is processing and ADC
// message but it has "forgotten" about the IO expander or any other
// device
// else if the message completed run the callback handle in the current
// context
// else do the next part of the message, etc.
}
Thanks!
What about to put all your devies initialisation to 'aggregate' function and call it when need?
void init_all_i2c_devices(void) {
initExtIOExpander();
initExtADC();
...
}
interrupt i2c() {
....
init_all_i2c_devices();// or i2c_init_callback()
}
main() {
init_i2c();
init_all_i2c_devices();
// set_i2c_init_callback(init_all_i2c_devices) if need
}
For design patterns in the embedded context take a look here and here.
Especially the watchdog timer fits your case.
I think it is not the I2C module which should reset the HW.
Related
I am a newbie in CANOPEN. I wrote a program that read actual position via PDO1 (default is statusword + actual position).
void canopen_init() {
// code1 setup PDO mapping
nmtPreOperation();
disablePDO(PDO_TX1_CONFIG_COMM);
setTransmissionTypePDO(PDO_TX1_CONFIG_COMM, 1);
setInhibitTimePDO(PDO_TX1_CONFIG_COMM, 0);
setEventTimePDO(PDO_TX1_CONFIG_COMM, 0);
enablePDO(PDO_TX1_CONFIG_COMM);
setCyclePeriod(1000);
setSyncWindow(100);
//code 2: enable OPeration
readyToSwitchOn();
switchOn();
enableOperation();
motionStart();
// code 3
nmtActiveNode();
}
int main (void) {
canopen_init();
while {
delay_ms(1);
send_sync();
}
}
If I remove "code 2" (the servo is in Switch_on_disable status), i can read position each time sync send. But if i use "code 2", the driver has error "sync frame timeout". I dont know driver has problem or my code has problem. Does my code has problem? thank you!
I don't know what protocol stack this is or how it works, but these:
setCyclePeriod(1000);
setSyncWindow(100);
likely correspond to these OD entries :
Object 1006h: Communication cycle period (CiA 301 7.5.2.6)
Object 1007h: Synchronous window length (CiA 301 7.5.2.7)
They set the SYNC interval and time window for synchronous PDOs respectively. The latter is described by the standard as:
If the synchronous window length expires all synchronous TPDOs may be discarded and an EMCY message may be transmitted; all synchronous RPDOs may be discarded until the next SYNC message is received. Synchronous RPDO processing is resumed with the next SYNC message.
Now if you set this sync time window to 100us but have a sloppy busy-wait delay delay_ms(1), then that doesn't add up. If you write zero to Object 1007h, you disable the sync window feature. I suppose setSyncWindow(0); might do that. You can try to do that to see if that's the issue. If so, you have to drop your busy-wait in favour for proper hardware timers, one for the SYNC period and one for PDO timeout (if you must use that feature).
Problem fixed. Due to much EMI from servo, that make my controller didn't work properly. After isolating, it worked very well :)!
I have an issue of an if statement not passing whilst my system gatt connection is not made.
Context
I have a BLE system using a NRF52840-dk board programmed in C. I also have a mobile application which, communicates with this board via a Gatt connection. I have a single service with a single characteristic. I write to this characteristic from my mobile application and, from this do some processing. At the moment I can send over a timestamp and begin storing data. However, I need to then send data back to my mobile device by this connection.
So what I have is a command to be sent from the phone to ask for some data. This should then send data back to the phone by changing the characteristic value.
Before I can change the value I need to see if the command has been issued. However, due to the priorities and constraints of the device I need to do this processing in the main function not in the BLE interrupt that I have done my time stamping in. This is due to the data I will be transmitting eventually will be large.
My issue however is, I receive the command to send some data back to the phone and update a global int value (changed from 0 to 1). Then in my main loop test this value and, if it is 1 write to the terminal and change the value back. I would then use this point of the code to run a function to send the data.
But this statement does not pass.
This is my main loop code
if(GATT_CONNECTED == false)//This works!
{
//Do some functions here
}
else if (GATT_CONNECTED == true)// GATT_CONNECTED = true
{
NRF_LOG_INFO("Test1 passed");//Testing variable this does not print
if(main_test == 1)
{
NRF_LOG_INFO("Test2 passed");//This does not print either irrelevant of value
main_test = 0;//False
}
idle_state_handle();
}
I don't know if the issue is the way I have defined my variable or due to interrupt priorities or something like that. But, when my Gatt connection is made the loop of (GATT_CONNECTED == true) does not seem to process.
My variable is defined in another file where my GATT connection is handled. The GATT connected variable is handled in main. my main_test variable is defined in another c file as int main_test = 0;. In the header declared as extern int main_test;.
I know the GATT_CONNECTED variable works as I have code in it that only runs when my gatt is not connected. I have omitted it for simplicity.
Any ideas,
Thanks
Ps Hope you are all keeping well and, safe
Edit
Added code for simplicity
main.c
bool GATT_CONNECTED = false;
int main(void)
{
Init_Routine();
while(1)
{
Process_data();//This runs if the gatt is not connected if statement inside
if(GATT_CONNECTED == true)//This does not run true when the gatt is connected
{
NRF_LOG_INFO("check gatt connectedpassed");//Testing variable.
nrf_gpio_pin_set(LED_4);//Turn an LED on once led 4 does not work
}
idle_state_handle();
}
}
I'm using ASF4 API hal_timer for a ARM Cortex M4. I'm using the timer driver to timing a data sequence.
Why does no reset function exist? I'm using the timer on a TIMER_TASK_ONE_SHOT mode and want to reset it when ever I need to.
I thought a simple
timer_start(&TIMER_0);
timer_stop(&TIMER_0);
would do the trick but does not seem to work.
Is it necessary to re-initialize the timer for each timing event?
I'm probably missing something obvious. Am I approaching this problem incorrectly reason being why the method timer_reset() doesn't exist?
I have no experience of this API, but looking at the documentation it is apparent that a single timer can have multiple tasks on different periods, so resetting TIMER_0 makes little semantic sense; rather you need to reset the individual timer task attached to the timer - of which there may be more than one.
From the documentation (which is poor and contains errors), and the source code which is more reliable:
timer_task_instance.time_label = TIMER_0.time ;
where the timer_task_instance is the struct timer_task instance you want to reset. This sets the start time to the current time.
Probably best to wrap that in a function:
// Restart current interval, return interval.
uint32_t timer_restart( struct timer_descriptor* desc, struct timer_task* tsk )
{
tsk->time_label = desc->time
return tsk->interval ;
}
Then:
timer_restart( &TIMER_0, &timer_task_instance ) ;
Assuming you're using the (edited) example from the ASF4 Reference Manual:
/* TIMER_0 example */
static struct timer_task TIMER_0_task;
static void TIMER_0_task_cb(const struct timer_task *const timer_task)
{
// task you want to delay using non-existent reset function.
}
void TIMER_0_example(void)
{
TIMER_0_task.interval = 100;
TIMER_0_task.cb = TIMER_0_task_cb;
TIMER_0_task.mode = TIMER_TASK_ONE_SHOT;
timer_add_task(&TIMER_0, &TIMER_0_task);
timer_start(&TIMER_0);
}
Instead of resetting, which isn't supported by the API, you could use:
timer_remove_task(&TIMER_0, &TIMER_0_task);
timer_add_task(&TIMER_0, &TIMER_0_task);
which will effectively restart the delay associated with TIMER_0_task.
Under the hood, timer tasks are maintained as an ordered list, in order of when each task will expire, and using the functions provided by the API maintains the list order.
When trying to use a callback function for a DBus reply I get stuck on creating a good/well working main loop.
What I want to do is simple, make a DBus call and specify a function which should be called when the reply comes. This is because I do not want to block my thread for the whole time until a reply has been calculated and arrived.
I first use dbus_connection_send_with_reply(..) to get a DBusPendingCall, then i specify a callback function using dbus_pending_call_set_notify(..). Before doing this, when connecting to the bus, I have started another thread which should wait for a response and call the callback functions. I have found no examples at all and no very good documentation of how I should design a dbus main-loop like this. I have tried:
// Main dbus loop handling data transfer and callbacks..
void *dbus_main(void *args)
{
my_dbus dbus = (my_dbus)args;
while (MY_DBUS_STATUS_STOPPING != dbus->status
&& dbus_connection_read_write_dispatch(dbus->conn, -1))
;
return 0;
}
// Start the dbus main loop in a new thread
void dbus_main_start(my_dbus dbus) {
if (!pthread_create(&dbus->th, NULL, dbus_main, dbus)) {
// PRINT ERROR
}
}
My problem is two things:
I try to stop the app by setting the dbus->status flag to MY_DBUS_STATUS_STOPPING and waiting for the threads to join. This does not work if the thread is blocked in the dbus_connection_read_write_dispatch(..) function. If i want the app to stop fast then I need to specify a very short timeout. Can't I wake the blocked thread in some other way?
More seriously, with this code i don't get any callback from the method I call. If I add some fprintf(..) to write to stdout I might suddenly get my callback. It seems quite random, so maybe some kind of deadlock? I have tried having a dbus_connection_flush(..) between sending the message and adding the callback with _set_notify(..) function. Doesn't do any difference... But printing some letters to stdout in the same place fixes the problem. Printing to stdout in the dbus-main-loop insted of an empty ";" seems to do the trick sometimes...
So anyone who has an example of using the low-level dbus api together with async methods, ie not using _block(..)??
You can create a simple DBus application as follows...
To setup a server to handle incoming messages, call dbus_connection_register_object_path passing in a VTable containing function pointers to handle the messages. Such as:
{ .unregister_function = UnregisteredMessage, .message_function = ServiceMessage }
To send a new message, call dbus_connection_send_with_reply and then dbus_pending_call_set_notify to associate a callback function to handle the reply.
Next you will need to service DBus. This can be done in a separate thread or by calling periodically with non-blocking calls in the same thread, as shown below:
/* Non-blocking read of the next available message */
dbus_connection_read_write ( MyDBusConnection, 0 ) ;
while ( dbus_connection_get_dispatch_status ( MyDBusConnection ) == DBUS_DISPATCH_DATA_REMAINS )
{
dbus_connection_dispatch ( MyDBusConnection ) ;
}
There are some good example of using the DBUS C API here: http://www.matthew.ath.cx/misc/dbus
It is highly recommended that you use a D-Bus library other than libdbus, as libdbus is fiddly to use correctly, as you are finding. If possible, use GDBus or QtDBus instead, as they are much higher-level bindings which are easier to use. If you need a lower-level binding, sd-bus is more modern than libdbus.
If you use GDBus, you can use GMainLoop to implement a main loop. If you use sd-bus, you can use sd-event.
I am trying to write a Linux kernel module to work similar to a 'MouseKeys' application - to grab certain keys and then output mouse events. I'm currently struggling trying to grab input in such a way that I can discard or passthrough keys based on some internal state of my module - what I want is something like:
int keyboard_callback(event) {
if (active) {
processKey(event); // this would output mouse events
return BLOCK_KEY_EVENT; // input is not visible to any applications
}
// input would be processed downstream as normal
return PASSTHROUGH_KEY_EVENT;
}
....
register_input_handler(keyboard_callback);