How do I prevent the Raspberry Pi Pico from taking ALL background actions and interrupts to create a pulse in software? - interrupt

My goal is to create a single pulse with the RPI pico by clearing and setting a hardware pin in software. I am attempting to do this in software because I did not see a way to provide a single non-repetitive pulse through one of the timer channels.
The resolution of the pulse width can be as much as 32ns, which should be easy to achieve with a 125MHz clock. Any pulse longer than 1us on the hardware pin will physically destroy the circuit.
In the simplest form the code should initialize a pin to the high state, pull the pin low, wait, and then set the pin high. There should be a way to predictably adjust the time between the low and high states.
In the code below, I would expect that every NOP between the gpio_clr and gpio_set commands increased the pulse width by either 8ns or 16ns. However, there is no consistency to the relation between pulse width and the number of NOP instructions between the gpio_clr and gpio_set commands. Sometimes it is 16ns, other times it is nearly a microsecond.
I have tried porting the C code to assembly and it did not change the outcome. Neither did save_and_disable_interrupts(). When placed into a while loop, the first pulse is usually over a microsecond and the other pulses are usually a consistent width.
When I view the disassembly of the C code, there are no instructions between the gpio_clr and gpio_set functions.
I have the impression that the RPI is taking a background action in between the setting and clearing of the pin. I am hoping someone knows how to make this code execute sequentially as it was written.
I would accept an answer that demonstrated how to use a timer channel to provide a single, non-repetitive pulse with 32ns of resolution. The alarm functions seem to have approximately 7us of overhead which makes them unusable.
#include "pico/stdlib.h"
#include "hardware/sync.h"
int main() {
const uint pulse_len = 1;
gpio_init(6);
sio_hw->gpio_set = 1u<<6;
gpio_set_dir(6, GPIO_OUT);
sio_hw->gpio_set = 1u<<6;
uint i=0;
//__asm("cpsid if");
uint32_t istatus = save_and_disable_interrupts();
//provide a single pulse
sio_hw->gpio_clr = 1u<<6;
//__asm("nop"); I would expect each NOP to increase pulse width by the same amount
//__asm("nop");
//__asm("nop");
//__asm("nop");
//__asm("nop");
//__asm("nop");
sio_hw->gpio_set = 1u<<6;
restore_interrupts(istatus);
//__asm("cpsie if");
while(true);
}

Related

Can't get the analogue watchdog to trigger an interrupt on the DFSDM peripheral of a STM32L475

I have an AMC1306 current shunt modulator feeding 1-bit PDM data at 10 MHz into a STM32L475. Filter0 takes the bit stream from Channel0 and applies a sinc3 filter with Fosr=125 and Iosr=4. This provides 24-bit data at 20 kHz and is working fine. The DMA transfers the data into a 1-word circular buffer in main memory to maintain fresh data.
I want to be able to call an interrupt function if the 24-bit value leaves a certain window. This would be caused in an over-voltage situation and needs to disengage the MOSFET driver. It would seem this functionality is offered by the analogue watchdog within the peripheral.
I am using STM32CubeIDE and the graphical interface within the IDE to configure the peripherals. Filter0 global interrupts are enabled. I have added this code:
/* USER CODE BEGIN 2 */
HAL_DFSDM_FilterRegularStart_DMA(&hdfsdm1_filter0, Vbus_DMA, 1);
// Set up the watchdog
DFSDM_Filter_AwdParamTypeDef awdParamFilter0;
awdParamFilter0.DataSource = DFSDM_FILTER_AWD_FILTER_DATA;
awdParamFilter0.Channel = DFSDM_CHANNEL_0;
awdParamFilter0.HighBreakSignal = DFSDM_NO_BREAK_SIGNAL;
awdParamFilter0.HighThreshold = 250;
awdParamFilter0.LowBreakSignal = DFSDM_NO_BREAK_SIGNAL;
awdParamFilter0.LowThreshold = -250;
HAL_DFSDM_FilterAwdStart_IT(&hdfsdm1_filter0, &awdParamFilter0);
/* USER CODE END 2 */
I have also used the HAL callback function
/* USER CODE BEGIN 4 */
void HAL_DFSDM_FilterAwdCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter, uint32_t Channel, uint32_t Threshold)
{
HAL_GPIO_WritePin(GPIOA, LED_Pin, GPIO_PIN_SET);
}
/* USER CODE END 4 */
But the callback function never runs! I have experimented with the thresholds (I even made them zero).
In the debugger I can see the AWDIE=0x1 (So the AWD interrupt is enabled). The AWDF = 0x1 (So the threshold has been crossed and the peripheral should be requesting an interrupt...). The code doesn't even trigger a breakpoint in the stm32l4xx_it.c filter0 interrupt. So it'd seem no DFSDM1_FLT0 interrupts are happening
I'd be enormously appreciative of any help, any example code, any resources to read. Thanks in advance.
I know the DMA conversion complete callbacks work
I have played around with various thresholds and note that the AWDF gets set when the threshold is crossed.

Can't get my DAC(PT8211) to work correctly using a PIC32MX uc and SPI

I'm just trying to learn to use external ADC and DAC (PT8211) with my PIC32MX534f06h.
So far, my code is just about sampling a signal with my ADC every time a timer-interrupt is triggered, then sending then same signal out to the DAC.
The interrupt and ADC part works fine and have been tested independently, but the voltages that my DAC outputs don't make much sens to me and stay at 2,5V (it's powered at 0 - 5V).
I've tried to feed the DAC various values ranging from 0 to 65534 (16bits DAC so i guess it should be the expected range of the values to feed to it, right?) voltage stays at 2.5V.
I've tried changing the SPI configuration, using different SPIs (3 and 4) and DACs (I have one soldered to my pcb, soldered to SPI3, and one one breadboard, linked to SPI4 in case the one soldered on my board was defective).
I made sure that the chip selection line works as expected.
I couldn't see the data and clock that are transmissed since i don't have a scope yet.
I'm a bit out of ideas now.
Chip selection and SPI configuration settings
signed short adc_value;
signed short DAC_output_value;
int Empty_SPI3_buffer;
#define Chip_Select_DAC_Set() {LATDSET=_LATE_LATE0_MASK;}
#define Chip_Select_DAC_Clr() {LATDCLR=_LATE_LATE0_MASK;}
#define SPI4_CONF 0b1000010100100000 // SPI on, 16-bit master,CKE=1,CKP=0
#define SPI4_BAUD 100 // clock divider
DAC output function
//output to external DAC
void DAC_Output(signed int valueDAC) {
INTDisableInterrupts();
Chip_Select_DAC_Clr();
while(!SPI4STATbits.SPITBE); // wait for TX buffer to empty
SPI4BUF=valueDAC; // write byte to TX buffer
while(!SPI4STATbits.SPIRBF); // wait for RX buffer to fill
Empty_SPI3_buffer=SPI4BUF; // read RX buffer
Chip_Select_DAC_Set();
INTEnableInterrupts();
}
ISR sampling the data, triggered by Timer1. This works fine.
ADC_input inputs the data in the global variable adc_value (12 bits, signed)
//ISR to sample data
void __ISR( _TIMER_1_VECTOR, IPL7SRS) Test_data_sampling_in( void)
{
IFS0bits.T1IF = 0;
ADC_Input();
//rescale the signed 12 bit audio values to unsigned 16 bits wide values
DAC_output_value = adc_value + 2048; //first unsign the signed 12 bit values (between 0 - 4096, center 2048)
DAC_output_value = DAC_output_value *16; // the scale between 12 and 16 bits is actually 16=65536/4096
DAC_Output(DAC_output_value);
}
main function with SPI, IO, Timer configuration
void main() {
SPI4CON = SPI4_CONF;
SPI4BRG = SPI4_BAUD;
TRISE = 0b00100000;
TRISD = 0b000000110100;
TRISG = 0b0010000000;
LATD = 0x0;
SYSTEMConfigPerformance(80000000L); //
INTCONSET = _INTCON_MVEC_MASK; /* Set the interrupt controller for multi-vector mode */
//
T1CONbits.TON = 0; /* turn off Timer 1 */
T1CONbits.TCKPS = 0b11; /* pre-scale = 1:1 (T1CLKIN = 80MHz (?) ) */
PR1 = 1816; /* T1 period ~ ? */
TMR1 = 0; /* clear Timer 1 counter */
//
IPC1bits.T1IP = 7; /* Set Timer 1 interrupt priority to 7 */
IFS0bits.T1IF = 0; /* Reset the Timer 1 interrupt flag */
IEC0bits.T1IE = 1; /* Enable interrupts from Timer 1 */
T1CONbits.TON = 1; /* Enable Timer 1 peripheral */
INTEnableInterrupts();
while (1){
}
}
I would expect to see the voltage at the ouput of my DAC to mimic those I put at the input of my ADC, instead the DAC output value is always constant, no matter what I input to the ADC
What am i missing?
Also, when turning the SPIs on, should I still manually manage the IO configuration of the SDI SDO SCK pins using TRIS or is it automatically taken care of?
First of all I agree that the documentation I first found for PT8211 is rather poor. I found extended documentation here. Your DAC (PT8211) is actually an I2S device, not SPI. WS is not chip select, it is word select (left/right channel). In I2S, If you are setting WS to 0, that means the left channel. However it looks like in the extended datasheet I found that WS 0 is actually right channel (go figure).
The PIC you've chosen doesn't seem to have any I2S hardware so you might have to bit bash it. There is a lot of info on I2S though ,see I2S bus specification .
There are some slight differences with SPI and I2C. Notice that the first bit is when WS transitions from high to low is the LSB of the right channel. and when WS transitions from low to high, it is not the LSB of the left channel. Note that the output should be between 0.4v to 2.4v (I2S standard), not between 0 and 5V. (Max is 2.5V which is what you've been seeing).
I2S
Basically, I'd try it with the proper protocol first with a bit bashing algorithm with continuous flip flopping between a left/right channel.
First of all, thanks a lot for your comment. It helps a lot to know that i'm not looking at a SPI transmission and that explains why it's not working.
A few reflexions about it
I googled Bit bashing (banging?) and it seems to be CPU intensive, which I would definately try to avoid
I have seen a (successful) projet (in MikroC) where someone transmit data from that exact same PIC, to the same DAC, using SPI, with apparently no problems whatsoever So i guess it SHOULD work, somehow?
Maybe he's transforming the data so that it works? here is the code he's using, I'm not sure what happens with the F15 bit toggle, I was thinking that it was done to manage the LSB shift problem. Here is the piece of (working) MikroC code that i'm talking about
valueDAC = valueDAC + 32768;
valueDAC.F15 =~ valueDAC.F15;
Chip_Select_DAC = 0;
SPI3_Write(valueDAC);
Chip_Select_DAC = 1;
From my understanding, the two biggest differences between SPI and I2S is that SPI sends "bursts" of data where I2S continuously sends data. Another difference is that data sent after the word change state is the LSB of the last word.
So i was thinking that my SPI is triggered by a timer, which is always the same, so even if the data is not sent continuously, it will just make the sound wave a bit more 'aliased' and if it's triggered regularly enough (say at 44Mhz), it should not be SO different from sending I2S data at the same frequency, right?
If that is so, and I undertand correctly, the "only" problem left is to manage the LSB-next-word-MSB place problem, but i thought that the LSB is virtually negligible over 16bit values, so if I could just bitshift my value to the right and then just fix the LSB value to 0 or 1, the error would be small, and the format would be right.
Does it sounds like I have a valid 'Mc-Gyver-I2S-from-my-SPI' or am I forgetting something important?
I have tried to implement it, so far without success, but I need to check my SPI configuration since i'm not sure that it's configured correctly
Here is the code so far
SPI config
#define Chip_Select_DAC_Set() {LATDSET=_LATE_LATE0_MASK;}
#define Chip_Select_DAC_Clr() {LATDCLR=_LATE_LATE0_MASK;}
#define SPI4_CONF 0b1000010100100000
#define SPI4_BAUD 20
DAaC output function
//output audio to external DAC
void DAC_Output(signed int valueDAC) {
INTDisableInterrupts();
valueDAC = valueDAC >> 1; // put the MSB of ValueDAC 1 bit to the right (becase the MSB of what is transmitted will be seen by the DAC as the LSB of the last value, after a word select change)
//Left channel
Chip_Select_DAC_Set(); // Select left channel
SPI4BUF=valueDAC;
while(!SPI4STATbits.SPITBE); // wait for TX buffer to empty
SPI4BUF=valueDAC; // write 16-bits word to TX buffer
while(!SPI4STATbits.SPIRBF); // wait for RX buffer to fill
Empty_SPI3_buffer=SPI4BUF; // read RX buffer (don't know why we need to do this here, but we do)
//SPI3_Write(valueDAC); MikroC option
// Right channel
Chip_Select_DAC_Clr();
SPI4BUF=valueDAC;
while(!SPI4STATbits.SPITBE); // wait for TX buffer to empty
SPI4BUF=valueDAC; // write 16-bits word to TX buffer
while(!SPI4STATbits.SPIRBF); // wait for RX buffer to fill
Empty_SPI3_buffer=SPI4BUF;
INTEnableInterrupts();
}
The data I send here is signed, 16 bits range, I think you said that it's allright with this DAC, right?
Or maybe i could use framed SPI? the clock seems to be continous in this mode, but I would still have the LSB MSB shifting problem to solve.
I'm a bit lost here, so any help would be cool

Canonical way(s) of determining system time in a microcontroller

Every so often I start a bare metal microcontroller project and end up implementing a system time measurement using a random timer unit.
I am working with ARM Cortex-M devices for a (albeit short) while now and typically used the SysTick ("System Tick") interrupt to create a 1ms resolution timer. It recently stumbled over a post that suggested chaining two Programmable Interrupt Timers (on a Kinetis KL25Z device) in order to create an interrupt-less 32bit millisecond timer, however sacrificing two PIT interrupts which may come in handy later on.
So I was wondering if there are some (sort of) canonical ways to determine the system time on a microcontroller - preferrably for Kinetis KL2xZ devices as I currently work with these, but not necessarily so.
The canonical method as you put it is exactly as you have done - using systick. That is the single timer device defined by the Cortex-M architecture; any other timer hardware is external to the core and vendor specific.
Some parts (STM32F2 for example) include 32 bit timer/counter hardware, so you would not need to chain two.
The best approach is to abstract timer services by defining a generic timer API that you implement for all parts you need so that the application layer is identical for all parts. For example in this case you might simply implement the standard library clock() function and define CLOCKS_PER_SEC.
If you are using two free-running cascaded timers, you must ensure high/low word consistency when combining the two counter values:
#include <time.h>
clock_t clock( void )
{
uint16_t low_word = 0 ;
uint16_t hi_word = 0 ;
do
{
hi_word = readTimerH() ;
lo_word = readTimerL() ;
} while( hi_word != readTimerH() ) ;
return (clock_t)(hi_word << 16 | lo_word) ;
}
I just looked into KL25 Sub-Family Reference Manual.
In Chapter 34 Real Time Clock (RTC) section 34.3.2 Time counter (may differ with document version).
I found that there are Two registers for Timer counter in RTC
32-bit seconds counter
16-bit prescaler register that increments once every 32.768 kHz clock cycle
Reference Manual says
Always write to the prescaler register before writing to the seconds register,
because the seconds register increments on the falling edge of bit 14 of the prescaler
register.
Which means to calculate system time, read rtc_sec_counter and add 14 bits of prescalar_reg
you can even create a macro to give you system time in uSec and mSec from combination of rtc_sec_counter and prescalar_reg or Sec(obviously from rtc_sec_counter)
For 16 bit prescalar REG System clock is 32.768 Khz, with this we can create macros to get time in uSec and mSec
#define PRESCALAR_TICK 32768
#define KHZ 1000
#define MHZ 1000000
/// Here first we extract 14bit value of prescalar_reg and than multiply it with MHZ to get better precision
/// but this value will not go more than 14 Bit
#define GET_SYS_US ((((prescalar_reg & 0x03FFF)*MHZ)/PRESCALAR_TICK))
#define GET_SYS_MS (GET_SYS_US)/KHZ)
if you need time in milliseconds up to 32 bit use below macro
#define GET_SYS_US_32bit ((rtc_sec_counter * 0x3FFF) + GET_SYS_US)
#define GET_SYS_MS_32bit ((rtc_sec_counter * 0x3FFF) + GET_SYS_MS)
But to use these information you must initialise RTC of you micro (Obviously)

How to make Timer1 more accurate as a real time clock?

I have PIC18F87J11 with 8 MHz oscillator and I am using timer1 as real time clock. At this moment I have it toggle an LED every 1 minute. I noticed it does work perfect fine the first few times but slowly it starts toggling the LED every 59 seconds. Then every few minutes it keeps going down to 58, 57, etc. I don't know if its impossible to get an accurate clock using internal oscillator or if I need external oscillator. My settings look right for timer1, I just hope I can resolve this issue with the current hardware.
Prescaler 1:8, TMR1 Preload = 15536, Actual Interrupt Time : 200 ms
// Timer 1 Settings
RCONbits.IPEN = 1; // Enable interrupt system priority feature
INTCONbits.GIEL = 1; // Enable low priority interrupts
// 1:8 prescalar
T1CONbits.T1CKPS1 = 1;
T1CONbits.T1CKPS0 = 1;
// Use Internal Clock
T1CONbits.TMR1CS = 0;
// Timer1 overflow interrupt
PIE1bits.TMR1IE = 1;
IPR1bits.TMR1IP = 0; // Timer 1 -> Low priority interrupt group
PIE1bits.TMR1IE = 1; // Enable Timer1 interrupt
// TMR1 Preload = 15536;
TMR1H = 0x3C;
TMR1L = 0xB0;
Interrupt Routine
void interrupt low_priority lowISR(void) {
if (PIR1bits.TMR1IF == 1) {
oneSecond++;
if (oneSecond == 5) {
minute_Counter++;
if (minute_Counter >= 60) {
// One minute passed
Printf("\r\n One minute Passed");
ToggleLed();
minute_Counter = 0;
}
oneSecond = 0;
}
// TMR1 Preload = 15536;
TMR1H = 0x3C;
TMR1L = 0xB0;
PIR1bits.TMR1IF = 0;
}}
The internal oscillator is a simple RC oscilator (a resistor/capacitor time constant determines its frequency), this kind of circuit may be accurate to only +/-10% over the operating temperature range of the device, and the device will be self-heating due to normal operating power dissipation.
From the data sheet:
An external crystal or other accurate external clock source is required to get accurate timing. Alternatively, if you have some other stable and accurate, but low frequency clock source, such as output from an RTC with a 38768 Hz crystal, you can use that to calibrate the internal RC oscillator and dynamically adjust it with the OSCTUNE register - by using a timer gated by the low frequency source, you can determine the actual frequency of INTOSC and adjust accordingly - it will not be perfect, but it will be better - but no better than the precision of the calibrating source of course.
Some devices have a die temperature sensor that can also be used to compensate, but that is not available on your device.
The RC error can cause serial communications mistiming to the extent that you cannot communicate with a device using asynchronous (UART) serial comms.
There are some stuff in the datasheet you linked, "2.5.3 INTERNAL OSCILLATOR OUTPUT FREQUENCY AND TUNING", on p38
The datasheet says that
The INTOSC frequency may drift as VDD or temperature changes".
Are VDD and temperature stable ?
It notes three ways to deal with this by tuning the OSCTUNE register. The three of them would need an external "oscillator" :
dealing with errors of EUSART...this signal should come from somewhere.
a peripheral clock
cpp module in capture mode. You may use any stable AC signal as input.
Good luck !
Reload the Timer as soon as it expires, the delay between timer overflow and rearm is affecting the total time. So this will solve your problem.
void interrupt low_priority lowISR(void)
{
if (PIR1bits.TMR1IF)
{
PIR1bits.TMR1IF = 0;
TMR1H = 0x3C;
TMR1L = 0xAF;
/* rest of the code here */
. . . .
}
}
One more recommendation is not to load up the isr, keep it simple.
For all timing, time and frequency applications the first and most important thing to do is to CALIBRATE THE CRYSTAL OSCILLATOR!!! The oscillator itself and its crystal MUST run exactly (to better than 1 part per million = 1ppm) of its nominal frequency. Crystals straight out of a factory (except some very specialized and expensive ones = 100's of $) are not running exactly at their nominal frequency. If the calibration is not done, all time and frequency related functions will be off, because the oscillator frequency is used as reference for all PICs internal functions. The calibration must be done against an accurate frequency counter by adjusting one of the capacitors from crystal pins to ground. Any processor routines for frequency (and time) calibration are not accurate enough.

Use of timer interrupts in arduino stop the serial library functions why?

I am working with an arduino project.I am using timer interrupts and Serial communication.But as soon as the timer interrupts enables arduino Serial library functions are not working.I am stuck with this problem. Is there any way to do this. I want to use both Serial communication and timer interrupts.Use of the following function stops the Serial communication
void initialize()
{
//timer0
TIMSK0 = 2;
OCR0A = 125;
TCCR0A = 0b00000010; //commenting TCCR0A = 0b00000010; and TIMSK1 = 1 ; enable
TCCR0B = 0b00000011; // the serial communications
//timer1
TCCR1B = 1 ;
TIMSK1 = 1 ;
//timer2
TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(WGM22) | _BV(CS20);
OCR2A = B11000111;
EICRA = 63 ;
EIMSK = (1 << INT0) | (1 << INT1);
}
I would avoid using Timer0, directly. As it will mess with Arduino Core Libraries, as you are seeing.
On initial glance I would suggest using a proven library such as SimpleTimer(). It will setup and manage multiple events where its "run" basically pulls the millis() from timer 0. But read farther down.
I recall that Timer0 is setup by the core library to overrun at 1K creating interrupt. Where the micros() function read the value within timer0 between millisecond interrupts.
And for using Timer1 you can try TimerOne() library. There are also TimerTwo, 3 and etc.. out there.
You may want to read through Ken Shirriff's Arduino-IRremote library. As it does much of what you want, in discrete methods. Such as the 40Khz PWM. Rather than depending upon other libraries. Where his original library uses a
USECPERTICK 50 // microseconds per clock interrupt tick
to read and sample the receive input from a IR demodulator, as to decode the frames.
I would also point out microtherion's fork of the library, as it uses pin change interrupts to get more accurate pin changes. Where his library, again discretely manages these interrupts.
Were as one could use PinChangeInt Library to setup your implementation. Where the individual pin changes' ISR could capture the time stamp almost immediately. Minus latency where in this case is much less the 50ms desired resolution.
And if you really needed more resolution you can use the Input Capture Function. As demonstrated in InputCapture.ino. Which will capture the time of transition in real-time and generate an ISR for latent handling.
From these examples you should be able to implement your ultra sonic sensor.
I had the same problem, so i suggest to:
Use the TimerOne() library.
Use flags in the timer, so you can control when the time that you
programed has past.
In the loop function, you should use only the Serial.available(),
so the time would be as close as possible to what you want.
Dont write too much code in the loop function and control the
sensors reading with a switch or if function.
Its not the best solution, but it works. You have to be careful with the time programed, it should be higher than the the time expended in the data reading.