I just wanted to know if I did this right:
I created a task on the 2nd core and inserted an interrupt in it.
xTaskCreatePinnedToCore(coreTask1, "CPU_1", 1000, NULL, 0, &Core1TaskHnd, 1); //Creating Task on Core 1
void coreTask1(void *parameter) //The Task I call
{
attachInterrupt(2, encoderEvent_Motor1, CHANGE);
//attachInterrupt(2, encoderEvent_Motor2, CHANGE); //TODO
//attachInterrupt(2, encoderEvent_Motor3, CHANGE); //TODO
//attachInterrupt(2, encoderEvent_Motor4, CHANGE); //TODO
Serial.println(xPortGetCoreID());
while (1)
{
/* code */
}
}
My question is: Is the interrupt really running on the 2nd core.
According to ESP-IDF's documentation, "External Peripheral Interrupts":
Allocating an external interrupt will always allocate it on the core
that does the allocation.
So, yes, when you set up an interrupt handler, the interrupt will be handler by the core that set up the handler.
That said, it's unnecessary to create a task to pin it to the second core (core 1) as Arduino code automatically runs on that core. The first core (core 0) runs the network stack.
You can test this by calling xPortGetCoreID() in Setup() as you did in your task:
Serial.print("Current CPU core ");
Serial.println(xPortGetCoreID());
You should see "Current CPU core 1" as output (the cores are normally numbered 0 and 1).
You can also test that your interrupt handler is running on core 1 by calling this from it. For extra credit, have the interrupt handler increment a counter every time it runs on core 0 and then check the counter periodically from your main program and output a message if it's ever not zero.
Related
I have a project with a FreeRTOS application running on custom hardware (STM32G483VETS). The project generates two versions of the binary. App1 loads at 0x08000000,App2 (future enhancements and bug fixes) at 0x08040000.
At power on, the MCU boots into App1 running in flash. When instructed over the CAN bus, it disables all interrupts, de-initialises all peripherals, sets up the new stack pointer and jumps to App2 with a call such as:
jump_to_app(EXTENDED_START); // EXTENDED_START is 0x08040000
Code for jump_to_app:
typedef struct {
uint32_t stack_address; // Stack Pointer
application_t *function_ptr; // Program Counter
} jump_structure_t;
void jumpToApp(const uint32_t address)
{
const jump_structure_t *jump_vector_p = (jump_structure_t*) address;
DeInit_all_modules();
/* Jump, uses assembler to avoid stack optimisation */
asm("msr msp, %0; bx %1;" : : "r"(jump_vector_p->stack_address), "r"(jump_vector_p->function_ptr));
}
Code for disabling interrupts and de-initialising modules:
void DeInitAllModules()
{
/* Disable all interrupts */
__disable_irq();
/* De-initialise all modules in the opposite order they were
* initialised in main.c
*/
// MX_OPAMP3_Init();
HAL_OPAMP_DeInit(&hopamp3);
// MX_OPAMP1_Init();
HAL_OPAMP_DeInit(&hopamp1);
// MX_TIM16_Init();
HAL_TIM_PWM_DeInit(&htim16);
// MX_SPI4_Init();
HAL_SPI_DeInit(&hspi4);
// MX_SPI3_Init();
HAL_SPI_DeInit(&hspi3);
// MX_SPI2_Init();
HAL_SPI_DeInit(&hspi2);
// MX_SPI1_Init();
HAL_SPI_DeInit(&hspi1);
//MX_DMA_Init();
HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);
HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);
HAL_NVIC_DisableIRQ(DMA1_Channel3_IRQn);
HAL_NVIC_DisableIRQ(DMA1_Channel2_IRQn);
HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);
// MX_DAC3_Init();
HAL_DAC_DeInit(&hdac3);
// MX_COMP2_Init();
HAL_COMP_DeInit(&hcomp2);
// MX_ADC5_Init();
HAL_ADC_DeInit(&hadc5);
// MX_TIM8_Init();
HAL_TIM_PWM_DeInit(&htim8);
// MX_TIM3_Init();
HAL_TIM_Base_DeInit(&htim3);
// MX_TIM1_Init();
HAL_TIM_Base_DeInit(&htim1);
// MX_RTC_Init();
HAL_RTC_DeInit(&hrtc);
// MX_ADC1_Init();
HAL_ADC_DeInit(&hadc1);
// MX_FDCAN1_Init();
HAL_FDCAN_DeInit(&hfdcan1);
// MX_GPIO_Init();
HAL_GPIO_DeInit(USR_LED_GPIO_Port, USR_LED_Pin);
HAL_GPIO_DeInit(ERR_LED_GPIO_Port, ERR_LED_Pin);
__HAL_RCC_GPIOG_CLK_DISABLE();
__HAL_RCC_GPIOF_CLK_DISABLE();
__HAL_RCC_GPIOE_CLK_DISABLE();
__HAL_RCC_GPIOD_CLK_DISABLE();
__HAL_RCC_GPIOC_CLK_DISABLE();
__HAL_RCC_GPIOB_CLK_DISABLE();
__HAL_RCC_GPIOA_CLK_DISABLE();
HAL_DeInit();
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
}
Up until the point where the jump happens, the IDE is using the ELF to display the C code, variables etc. but App2 has a separate ELF.
How do I keep debugging after the jump? Can I 'combine' the ELF files?
I am developing in STM32CubeIDE v1.9.0 and debugging using a J-Link Ultra+.
Thanks
Can I 'combine' the ELF files?
Go to the debugging configuration (you find this this by clicking the downwards arrow right of the green bug symbol and selecting "Debug configurations" in the drop-down menu) and select the debugging configuration that you use for debugging.
Select the "Start" tab.
In the "Start" tab you can specify which ELF files are used for debugging and you can specify if these ELF files are used for Flashing or only for the debugging information.
How do I keep debugging after the jump?
At least entering the new application (this is the asm part...) I would debug on machine code (disassembly) level.
To do this, you check the "black 'i' with a yellow arrow to the right" button.
... sets up the new stack pointer ...
I have not much experience with FreeRTOS, but are you sure that the CONTROL register (which specifies if msp or psp is the stack pointer and if the CPU runs in privileged mode) has the correct value?
If the CPU is in privileged mode, you even have to enter an exception handler to modify the CONTROL register...
There is a screenshot in the Eclipse Wiki: I'm talking about ...
the "down arrow" next to the leftmost icon in the icon bar and
the third icon from the right.
I'm trying to read the linFrame sent from a slave LIN node, to identify when a particular bit has changed from from zero to one.
I'm sending a LIN message to a slave servo that commands it to move beyond a physical end-stop.
Once it physically hits the end-stop, its status message will set a single bit from zero to one. That bit identifies when the servo motor has stalled. My goal is to have the CAPL script detect that the motor has stalled.
My command message is sent by my CAPL script using the "output()" function. I'm unsure which function can read the response, but I think I have to send a header for the response message and then read the response frame.
variables
{
linFrame 0x03 ACT_CTRL_MT3 = {msgChannel=1}; // actuator control command
linFrame 0x21 ACT_STA_MT3 = {msgChannel=1}; // actuator status response
mstimer timer1;
}
on key 'c'
{
// command large angular position from servo at node 3
ACT_CTRL_MT3.RTR = 0;
ACT_CTRL_MT3.byte(0)=0x12; // section 4.5.9 of manual
ACT_CTRL_MT3.byte(1)=0xE4;
ACT_CTRL_MT3.byte(2)=0x14;
ACT_CTRL_MT3.byte(3)=0xFE; // max angle
ACT_CTRL_MT3.byte(4)=0xFF; // max angle
ACT_CTRL_MT3.byte(5)=0x00;
ACT_CTRL_MT3.byte(6)=0x00;
ACT_CTRL_MT3.byte(7)=0x40;
output(ACT_CTRL_MT3); // update command payload
// Send the frame header
ACT_CTRL_MT3.RTR = 1;
output(ACT_CTRL_MT3);
settimer(timer1,5000); // wait 5 seconds for motor to move
}
on timer timer1{
//send header of status message
ACT_STA_MT3.RTR = 1;
output(ACT_STA_MT3);
write("Reading message...");
//output message context
writelineex(1,1,"FrameId=%d Length=%d, 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X;", ACT_STA_MT3.ID, ACT_STA_MT3.DLC,
ACT_STA_MT3.byte(0), ACT_STA_MT3.byte(1), ACT_STA_MT3.byte(2), ACT_STA_MT3.byte(3), ACT_STA_MT3.byte(4), ACT_STA_MT3.byte(5), ACT_STA_MT3.byte(6), ACT_STA_MT3.byte(7));
}
The data that is written by the "writelinee" function is very different from the values seen in my LIN trace window in CANalyzer.
I find it especially strange that the ID field written out is different from the ID set in the variables section at the start of the code. In the code I define the frame ID of that status message as 0x21, but the write command gives a different value (0x35 I believe, although I'm away from my setup at the moment.
Unfortunately I do not have CANoe licence so I cannot check my code is functional. I have done similar thinkgs many times, so we should be good.
As I notices from your description you need to do there 3 things:
Send control LIN frame (ID 0x03) with some data to start the motor.
Send status LIN headers (ID 0x21) - remember that LIN Slave cannot initiate transmission on LIN bus so you need to provice LIN headers so LIN Slave can respond with some data.
Get response data from LIN header (data added by LIN slave to LIN header 0x21) - informs about motor stalling.
This is small modification to the code that you provided before. I also marked which things can be done in a different ways (you know CANoe have many features):
variables
{
linFrame 0x03 ACT_CTRL_MT3 = {msgChannel=1}; // actuator control command
linFrame 0x21 ACT_STA_MT3 = {msgChannel=1}; // actuator status response
mstimer timer1;
}
// starts the fun
on key 'c'
{
ACT_CTRL_MT3.RTR = 1; // RTR=1 means that you want to send entire message (header + data)
ACT_CTRL_MT3.byte(0)=0x12;
ACT_CTRL_MT3.byte(1)=0xE4;
ACT_CTRL_MT3.byte(2)=0x14;
ACT_CTRL_MT3.byte(3)=0xFE;
ACT_CTRL_MT3.byte(4)=0xFF;
ACT_CTRL_MT3.byte(5)=0x00;
ACT_CTRL_MT3.byte(6)=0x00;
ACT_CTRL_MT3.byte(7)=0x40;
output(ACT_CTRL_MT3); // this sends the message to the bus
settimer(timer1, 20); // set timer to trigger actions in 20 ms
}
// sending LIN headers (ID 0x21) cyclically
on timer timer1
{
// sends a single LIN header - you can also try to do it this way:
// ACT_STA_MT3.RTR=0;
// output(ACT_STA_MT3);
linTransmitHeader(ACT_STA_MT3); // or linTransmitHeader(0x21);
//set timer again to let LIN Slave respond if stalled later on
settimer(timer1, 20); // reset to execute every 20 ms
}
// event on receiving entire frame (header + response data) from LIN Slave
on linFrame ACT_STA_MT3
{
// not sure where is located your bit that is informing about stalling of the motor, but in this example I assumed it is on first position of byte 0
if (this.byte(0) & 0x01)
{
// you can put some other instructions here
write("Motor has stalled!");
}
// you can also log entire frame (I think your code was fine so I copied it):
writelineex(1,1,"FrameId=%d Length=%d, 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X;", this.ID, this.DLC, this.byte(0), this.byte(1), this.byte(2), this.byte(3), this.byte(4), this.byte(5), this.byte(6), this.byte(7));
}
PS. Try this and let me know if it compiles and works as expected as I literally wrote it in notepad. I will help you to fix potential errors.
About #Maciek answer, it actually works. Interessting to see that if you have your .ldf file in the "ldf explorer" by Vector with the correct signals assigned, then you could replace on linframe <frame> by on signal <any signal>.
The main difference is that linframe receives all the bytes, so you must use masks as #Maciek did here if (this.byte(0) & 0x01) which basically checks the first bit of the first byte. On the other hand, if you had a signal called e.g MyAlarmSignal which was defined in the .ldf file as the first bit of the frame, then you could simply read the signal using on signal MyAlarmSignal and inside the handler use $MyAlarmSignal or simply this to get the value.
System is basic but I have terrible problem and I can not solve it pls help me. When my system works PIC keep running but clear the registers 4-5 times in a day.
How system should work:
-I have a PIC, pneumatic cylinder and 3 sensor(works with 24V DC).
-Main sensor take the signal from another system.
-When a signal came from main sensor, if the cyclinder is backward, cylinder should go to forward until forward sensor see it and if the cylinder is forward, cyclinder should come to backward until backward sensor see it.
Program:
#include <16F628A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or
B5(PIC18) used for I/O
#use delay(crystal=4000000)
#use fast_io(a)
#use fast_io(b)
#define goForward PIN_A0
#define comeBackward PIN_A1
#define main_sensor PIN_B0
#define positionSensorForward PIN_B5
#define positionSensorBackward PIN_B4
int1 pistonPositionedForward=0, pistonPositionedBackward=1;
int1 positionForwardReg=0, positionBackwardReg=0;
int1 pistonForwarding=0, pistonBackwarding=0;
#priority rb,ext
#int_RB NOCLEAR
void B_change()
{
positionForwardReg=input(positionSensorForward);
positionBackwardReg=input(positionSensorBackward);
if(positionForwardReg&&pistonForwarding) //if forwarding and forward sensor see
{
disable_interrupts(INT_RB);
output_low(goForward);
pistonPositionedForward=1;
pistonPositionedBackward=0;
write_eeprom(0,1);
write_eeprom(1,0);
pistonForwarding=0;
pistonBackwarding=0;
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
}
else if(positionBackwardReg&&pistonBackwarding) //if backwarding and backward sensor see
{
disable_interrupts(INT_RB);
output_low(comeBackward);
pistonPositionedForward=0;
pistonPositionedBackward=1;
write_eeprom(0,0);
write_eeprom(1,1);
pistonForwarding=0;
pistonBackwarding=0;
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
}
clear_interrupt(int_rb);
}
#int_ext NOCLEAR
void ext_interrupt()
{
disable_interrupts(INT_EXT);
positionForwardReg=input(positionSensorForward);
positionBackwardReg=input(positionSensorBackward);
if(positionForwardReg^positionBackwardReg) //if one of position sensor is see then position according to sensor, else position according to memory
{
pistonPositionedForward=positionForwardReg;
pistonPositionedBackward=positionBackwardReg;
}
if(pistonPositionedForward)
{
pistonBackwarding=1;
pistonForwarding=0;
output_high(comeBackward);
clear_interrupt(int_rb);
enable_interrupts(INT_RB);
}
else if(pistonPositionedBackward)
{
pistonForwarding=1;
pistonBackwarding=0;
output_high(goForward);
clear_interrupt(int_rb);
enable_interrupts(INT_RB);
}
clear_interrupt(int_ext);
}
void main()
{
//to remember last position after power off
pistonPositionedForward=read_eeprom(0);
pistonPositionedBackward==read_eeprom(1);
set_tris_a(0x00);
set_tris_b(0xFF);
output_a(0x00);
delay_ms(1000);
ext_int_edge(L_TO_H);
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
while(TRUE)
{
}
}
And my circuit:
CIRCUIT
*j2,j3 connected selonoid valve
*J4,J5,J6 connected 3 sensors 1. pin +24VDC,2. pin GND, 3.pin sensor data
***B1 and B2 connections changed. Now B1 connected to B5,B2 connected to B4
And These are I tried:
-I have 3 PIC all of them do same thing
-I changed 24V power supply
-I cancelled 7805 and 7812 and I connected seperate 5V power supply istead of 7805.
I am debugging via LEDs. Sometimes system stop running just waiting at one of positions. Take main sensor signal but doesnot anything, And pistonPositionedForward and pistonPositionedBackward register values are 0. I cant find problem how can it clear these registers?
You have unconnected pins on RB that are configured as inputs, with no internal pull ups set. Electrical noise may well trigger unwanted interrupts on PORTB, that has been known to happen.
The use of interrupts is making the overall logic a bit hard to follow for such a simple device. Have you tried rewriting the program NOT using interrupts (except maybe for EXT)? It should not take long and I think it may greatly improve the reliability - and maintainability, without impacting performance of the physical system.
I suggest you first configure the unused PORTA and PORTB pins as outputs, and see if the problem goes away. If that fails, a rewrite not using interrupts should take no more than an hour. This would probably make sense since that is probably way shorter than the time you have already spent chasing the issue.
Reading the description, I came up with this solution.
#include <16F628A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B5(PIC18) used for I/O
#use delay(crystal=4000000)
#use fast_io(a)
#use fast_io(b)
#define FWD_MOVE PIN_A0
#define BACK_MOVE PIN_A1
#define PORTA_RESET (0x00) // outputs=LO, unused pins as outputs
#define PORTA_TRISTATE (0x00)
#define EXT_SENSOR PIN_B0
#define FWD_REST PIN_B5
#define BACK_REST PIN_B4
#define PORTB_RESET (0xCE) // can't use pull ups because of HW logic...
#define PORTB_TRISTATE (0x31)
#define EEPROM_STATUS_ADDR (0x0000)
#define EEPROM_STATUS_FWD (0x01)
#define EEPROM_STATUS_BACK (0x00)
int1 extLast;
int1 extCur;
void main()
{
// setup
output_a(PORTA_RESET):
output_b(PORTB_RESET):
// setting to last known state...
// safety check.
output_low(FWD_MOVE);
output_low(BACK_MOVE);
// This will activate the outputs to make sure we have good
// positioning.
switch(eeprom_read(EEPROM_STATUS_ADDR))
{
default: // EEPROM error... I'll let you decide what to do here.
// either move forward or back.
// this implementation goes back by default.
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_BACK);
disable_interrupts(GLOBAL);
// falling through...
case EEPROM_STATUS_BACK:
output_high(BACK_MOVE);
break;
case EEPROM_STATUS_FWD:
output_high(FWD_MOVE);
break;
}
// activate outputs... watch your fingers!
set_tris_a(PORTA_TRISTATE);
set_tris_b(PORTB_TRISTATE);
extLast = input(EXT_SENSOR);
for (;;)
{
// read external sensor, take action.
extCur = input(EXT_SENSOR);
if (extCur && !extlast)
{
// safety check.
output_low(FWD_MOVE);
output_low(BACK_MOVE);
// control logic
switch(eeprom_read(EEPROM_STATUS_ADDR))
{
default: // should never happen.
// falling through...
case EEPROM_STATUS_BACK:
output_high(FWD_MOVE);
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_FWD);
disable_interrupts(GLOBAL);
break;
case EEPROM_STATUS_FWD:
output_high(BACK_MOVE);
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_BACK);
disable_interrupts(GLOBAL);
break;
}
}
extLast = extCur;
// mechanical interface:
// read the limit guards and stop movement when done.
if (input(FWD_REST))
{
output_low(FWD_MOVE);
}
if (input(BACK_REST))
{
output_low(BACK_MOVE);
}
}
}
Of course, I could not check the above code on-site :).
After reviewing the schematics, I must also advise to add 1N4001 diodes in parallel of the 2 output MOSFETS to give them better protection against reverse voltage spikes. MOSFET built-in diodes are not very sturdy. 1N4148 or 1N914 would work there as well.
The 16F628 has very limited stack space. It looks like you are experiencing stack overflow during the call to write_eeprom. Calling write_eeprom from an interrupt may not be such a good idea after all.
There was a bug in older CCS compilers, related to the use of write_eeprom. It seems write_eeprom is enabling interrupts during the call. I've added calls to disable interrupts after the writes. I don't know if that bug was fixed, since I never use CCS.
[EDIT] after checking your HW. I realized you cannot use internal pull-ups because the HW logic is positive-going. The pull-ups in the PIC are meant to work with NPN transistors in the open collector configuration (emitter to ground). I changed the code accordingly.
The way you write to EEPROM is not good. eeprom writes take time, and the second write is usually taken care of in the eeprom interrupt. The CCS bug that enables the global interrupt and unmask the EEIE in eeprom_write does not help. Unhandled interrupts do generate a reset.
I am currently porting my DCF77 library (you may find the source code at GitHub) from Arduino (AVR based) to Arduino Due (ARM Cortex M3).
The library requires precise 1ms timing. An obvious candidate is the use of the systicks. Conveneniently the Arduino Due is already setup for systicks with 1 kHz.
However my (AVR) DCF77 library is capable to tune the timing once it locks to DCF77. This is done by manipulating the timer reload values like so
void isr_handler() {
cumulated_phase_deviation += adjust_pp16m;
// 1 / 250 / 64000 = 1 / 16 000 000
if (cumulated_phase_deviation >= 64000) {
cumulated_phase_deviation -= 64000;
// cumulated drift exceeds 1 timer step (4 microseconds)
// drop one timer step to realign
OCR2A = 248;
} else if (cumulated_phase_deviation <= -64000) {
// cumulated drift exceeds 1 timer step (4 microseconds)
// insert one timer step to realign
cumulated_phase_deviation += 64000;
OCR2A = 250;
} else {
// 249 + 1 == 250 == 250 000 / 1000 = (16 000 000 / 64) / 1000
OCR2A = 249;
}
DCF77_Clock_Controller::process_1_kHz_tick_data(the_input_provider());
}
I want to port this to the ARM processor. In the ARM information center I found the following documentation.
Configuring SysTick
...
To configure the SysTick you need to load the SysTick Reload Value
register with the interval required between SysTick events. The timer
interrupt or COUNTFLAG bit (in the SysTick Control and Status
register) is activated on the transition from 1 to 0, therefore it
activates every n+1 clock ticks. If a period of 100 is required 99
should be written to the SysTick Reload Value register. The SysTick
Reload Value register supports values between 1 and 0x00FFFFFF.
If you want to use the SysTick to generate an event at a timed
interval, for example 1ms, you can use the SysTick Calibration Value
Register to scale your value for the Reload register. The SysTick
Calibration Value Register is a read-only register that contains the
number of pulses for a period of 10ms, in the TENMS field (bits 0 to
23). This register also has a SKEW bit (30) that is used to indicate
that the calibration for 10ms in the TENMS section is not exactly 10ms
due to small variations in clock frequency. Bit 31 is used to indicate
if the reference clock is provided.
...
Unfortunately I did not find anything on how SysTick->LOAD and SysTick->CALIB are connected. That is: if I want to throttle or accelerate systicks, do I need to manipulate the LOAD or the CALIB value? And which values do I need to put into these registers?
Searching the internet did not bring up any better hints. Maybe I am searching at the wrong places.
Is there anywhere a more detailed reference for these questions? Or maybe even some good examples?
Comparing the AtMega328 datasheet with the Cortex-M3 TRM, the standout point is that the timers work opposite ways round: on the AVR, you're loading a value into OCR2A and waiting for the timer in TCNT2 to count up to it, whereas on the M3 you load the delay value into SYST_RVR, then the system will count down from this value to 0 in SYST_CVR.
The big difference for calibration is going to be because the comparison value is fixed at 0 and you can only adjust the reload value, you might have more latency compared to adjusting the comparison value directly (assuming the counter reload happens at the same time the interrupt is generated).
The read-only value in SYST_CALIB (if indeed it even exists, being implementation-defined and optional), is merely for relating SYSTICK ticks to actual wallclock time - when first initialising the timer, you need to know the tick frequency in order to pick an appropriate reload value for your desired period, so having a register field that says "this many reference clock ticks happen in 10ms (possibly)" offers some possibility of calculating that at runtime in a portable fashion, rather than having to hard-code a value that might need changing for different devices.
In this case, however, not only does having an even-more-accurate external clock to synchronise against makes this less important, but crucially, the firmware has already configured the timer for you. Thus you can assume that whatever value is in SYST_RVR represents close-enough-to-1KHz, and work from there - in fact to simply fine-tune the 1KHz period you don't even need to know what the actual value is, just do SysTick->LOAD++ or SysTick->LOAD-- if the error gets too big in either direction.
Delving a bit deeper, the SAM3X datasheet shows that for the particular M3 implementation in that SoC, SYSTICK has a 10.5 MHz reference clock, therefore the SYST_CALIB register should give a value of 105000 ticks for 10ms. Except it doesn't, because apparently Atmel thought it would be really clever to make the unambiguously-named TENMS field give the tick count for 1ms, 10500, instead. Wonderful.
Just for the reason that others do not have to dig around like had to do - here is what I found out in addition.
In arduino-1.5.8/hardware/arduino/sam/system/CMSIS/CMSIS/Include/core_cm*.h there is code to manipulate SysTick. In particular in core_cm3.h there is a function
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
Then in arduino-1.5.8/hardware/arduino/sam/variants/arduino_due_x/variant.cpp in function init there is
// Set Systick to 1ms interval, common to all SAM3 variants
if (SysTick_Config(SystemCoreClock / 1000))
{
// Capture error
while (true);
}
Since SystemCoreClock evaluates to 84000000 it follows that this compiles like SysTick_Config(84000). I verified against a DCF77 module that SysTick_Config(84001) will slow down SysTicks while SysTick_Config(83999) will speed it up.
I am writing a program which writes to a wireless device on the serial port. The firmware has a feature which will confirm the last message sent to the device was ok, so I'm trying to make use of that and update the settings readout on the GUI rather than ask the device for all of its settings every time I make a change. I think my code will explain it a little better:
// global variable
bool queryStatusOK;
//event handler response from serial port
this.QueryStatusReponseEvent += QueryStatusResponse;
private void QueryStatusResponse(byte[] packet)
{
if (packet[3] == 0) queryStatusOK = true;
else queryStatusOK = false;
}
public void setParameter(string device)
{
//send command to serial port to change a single device parameter
Thread.Sleep(100); //sleep thread so 2 commands are not sent at once
//send command to confirm previous command was received
Thread.Sleep(100); //sleep thread to give time for confirmation to receive
if (queryStatusOK)
{
//update GUI at this point
}
}
The program is not consistent. It works sometimes, but not always. Even if I extend the thread sleep to a full second to give the boolean time to update, it still sometimes will not hit it. Can anyone suggest a better way to do this?
Thanks!
Mike