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.
Related
I'm trying to write a program that puts my ESP32 into a deep sleep state, and uses touch input to wake it up. I'm able to put it into the deep sleep state, but as soon as it enters, it wakes up and never calls into the callback function.
Reading the raw data from the touch pad, it idles around 25k, and touch inputs from my hand give it a value of around 180k. The 100k value in the code snippet below is the threshold to where I'm comfortable to determine that a touch has been detected.
I'd like to point out that this is different from ext0 and ext1 wake ups.
static void touchsensor_interrupt_cb(void *arg)
{
... // code here turns on an LED and prints to serial
}
void setup(){
...
touch_pad_init();
touch_pad_config(TOUCH_PAD_NUM2);
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM2, 100000);
touch_pad_isr_register(touchsensor_interrupt_cb, NULL, TOUCH_PAD_INTR_MASK_ACTIVE);
touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_ACTIVE);
touch_pad_sleep_channel_enable(TOUCH_PAD_NUM2, true);
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
touch_pad_fsm_start();
esp_sleep_enable_touchpad_wakeup();
Serial.println("entering deep sleep");
esp_deep_sleep_start();
}
I've triple-checked that my circuit is correct. Running on an ESP32S3 Dev Kit v1.0. If there's a better place to post this please let me know.
The issue ended up being this line:
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM2, 100000);
The threshold here was relative to the raw sensor data. Instead, it should be relative to raw_data - benchmark, where (I'm assuming) the benchmark is computed from what it considers the sensor's baseline value. You can get your benchmark using touch_pad_sleep_channel_read_benchmark.
Printing out the expression above on raw, unfiltered data gives values like:
01:25:10.619 -> 3
01:25:10.696 -> -4
01:25:10.774 -> 3
01:25:10.851 -> -10
01:25:10.930 -> -4
01:25:11.006 -> 20
01:25:11.100 -> -2
01:25:11.177 -> 2
01:25:11.255 -> 1
01:25:11.333 -> 0
Where those values oscillate around 0 (the noise in my touch sensor). I changed the line above it to:
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM2, benchmark * threshold);
Where threshold is 0.2, meaning I'll consider a "touch" when the ESP32 reads sensor values of 120% of the benchmark value. I hope this helps someone.
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'm porting 3.14 to an ARM-based SOC which was successfully running the 3.2 kernel.
I'm getting stuck in the code that calibrates jiffies.
calibrate_delay_converge()-init/calibrate.c
/* wait for "start of" clock tick */
ticks = jiffies;
while (ticks == jiffies) <---- infinite loop waiting for jiffies to change
; /* nothing */
/* Go .. */
jiffies isn't being updated. Where does jiffies get updated? I'm looking for smoking gun code like jiffies++ or a .S file updating jiffies.
I went down the rabbit hole of the timers and interrupt system in Linux. The timer interrupt isn't enabled (in the PL190 HW). I'm hoping if I can track from the bottom up (where jiffies SHOULD be called), I can find why the interrupt(s) aren't being enabled.
Look at do_timer(). It was moved to kernel/time/timekeeping.c at some point in the past few years.
jiffies does not directly get incremented, it gets assigned the low order 32-bit of jiffies_64
/*
* The 64-bit jiffies value is not atomic - you MUST NOT read it
* without sampling the sequence number in xtime_lock.
* jiffies is defined in the linker script...
*/
void do_timer(unsigned long ticks)
{
jiffies_64 += ticks;
update_wall_time();
calc_global_load(ticks);
}
In 3.2 it is http://lxr.free-electrons.com/source/kernel/time/timekeeping.c?v=3.2#L1192
jiffies gets the value from jiffies_64 here in the machine specific file:
http://lxr.free-electrons.com/source/arch/arm/kernel/vmlinux.lds.S?v=3.2
36 #ifndef __ARMEB__
37 jiffies = jiffies_64;
38 #else
39 jiffies = jiffies_64 + 4;
40 #endif
The book ldd says for the function blk_queue_segment_boundary() like this:
Some devices cannot handle requests that cross a particular size
memory bound- ary; if your device is one of those, use this function
to tell the kernel about that boundary. For example, if your device
has trouble with requests that cross a 4- MB boundary, pass in a mask
of 0x3fffff. The default mask is 0xffffffff.
I don't quite understand what the boundary means here, for example, I have a virtual block device, which are made of indeed 4MB files, so I want a request not exceed 4MB boundary,
unsigned long sector = blk_rq_pos(req);
unsigned long offset = sector << 9;
unsigned long nbytes = blk_rq_bytes(req);
int file_offset = offset % (1 << 22);
What I want is that (file_offset + nbytes) not greater than 4M, but indeed sometimes it exceeds 4M,
so, is there any misunderstanding of blk_queue_segment_boundary() ?
Some controllers (particulary IDE) can not handle DMA requests that cross memory regions at 4MB. Think of it as segment:index addressing where index can not be larger that the set boundary.
There is also a blk_queue_max_segment_size. Both are used to construct correct requests to the device - requests get reordered and merged.
There are other uses. For example, from xen-blkfront.c:
/* Each segment in a request is up to an aligned page in size. */
blk_queue_segment_boundary(rq, PAGE_SIZE - 1);
blk_queue_max_segment_size(rq, PAGE_SIZE);
Requests are limited to PAGE_SIZE for better performance.
I am using a PIC32MX534F064L (datasheet), and trying to read several of its analog pins (marked AN0 to AN15).
With none of those pins connected to anything, I expect to read a value of zero. Instead on AN0 through AN5 I read values between 650 and 900. Only from the rest (AN6 through AN15) I get a value of zero.
When each of the pins is connected to a source, they report correctly. Each of the pins, AN0 through AN15 will report 0 for 0.0V, and 1023 for 3.3V.
I've tried sampling the values in pairs, and each separately. Whether sampled together or apart, AN0 will report non-zero values (usually around 700-800), and AN13 will report 0.
My first thought was that I somehow failed to properly set up the ADC. Here's my code:
#include <stdio.h>
#include <plib.h>
unsigned int an0;
unsigned int offset;
char buffer[100];
int main(void)
{
SYSTEMConfigPerformance(72000000L);
CloseADC10();
#define ADC_CONFIG1 ADC_MODULE_ON | ADC_FORMAT_INTG | \
ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON
#define ADC_CONFIG2 ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | \
ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_2 | \
ADC_ALT_BUF_ON | ADC_ALT_INPUT_ON
#define ADC_CONFIG3 ADC_CONV_CLK_INTERNAL_RC | ADC_SAMPLE_TIME_15
#define ADC_CONFIGSCAN SKIP_SCAN_ALL
#define ADC_CONFIGPORT ENABLE_AN0_ANA
SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN0 );
OpenADC10( ADC_CONFIG1, ADC_CONFIG2, ADC_CONFIG3, \
ADC_CONFIGPORT, ADC_CONFIGSCAN );
EnableADC10();
while ( ! mAD1GetIntFlag() ) { }
while (1)
{
offset = 8 * ((~ReadActiveBufferADC10() & 0x01));
an0 = ReadADC10(offset);
sprintf(buffer, "AN0 = %u", an0);
}
return 0;
}
Looking in the PIC's datasheet, I noticed two things:
The pins AN0 to AN5, the ones mis-reporting non-zero values, are also CNx pins. These pins are "Change Notification" pins, that are meant to raise an interrupt when the value on the pins changes.
There is a "weak pull-up" that can be enabled on all CNx pins.
So I tried disabling the "weak pull-up" by using this line:
mCNClose();
Which disables all the CNx pins and their pull-ups. Sadly, this did not help. And when I checked the value of the CN-pull-up-register (CNPUE
What else can I try? Am I doing something wrong in my code?
Well, your expectation is wrong!
The minimum input resistance for source should be only few kilo ohms check datasheet.
If ADC pins is floating (not connected) the unpredicted value of internal parasitic current will cause that measuring value will be bigger than 0. Remember the ADC sample capacitor has only few pF capacity so floating pins can oscillate in wide voltage range also from external EM (electromagnetic) influences.
So, connect at least 1M resistors to pull down voltage on ADC pin, the resistance of pull down resistor is depended of ADC sample time. If ADC sample time is short than decrease the pull down value of resistor.
EDIT:
Check datasheet page 214 parameter AD17: Recommended Impedance of Analog Voltage Source is 5 KOhms. And AD15 say that that max. Leakage Current on ADC input pins can be +/-0.61 uA.
It's probably just noise, since the inputs are high impedance when nothing is connected. Try grounding the inputs (connect to 0V) as an experiment - the values should then be close to 0. If you need the inputs to be zero when nothing is connected then connect a pull-down resistor to each input (between input and 0V) to lower the impedance - a value of 10k ohms should do it.
Do not leave pins unconnected! The unconnected pin is essentially an antenna which could pick up voltages outside of the Vss and Vdd range. Section 2.10 of the datasheet says to not leave any pins unconnected (or if you do, configure them as outputs and drive them low.)
If you want to test your A2D, you can configure the pin as a digital output (the analog setting only overrides the digital input) and then drive it high and low to test.