I want to offload the CPU of my STM32G491 by using the DMA function.
I want to use the Timer 2 (TIM2) to generate four moments where a DMA transfer is needed.
While doing so I can create two pulses in one period.
The period, duty cycle, and the delay are adjustable by using the Timer Output Compare functions.
int amplitude = 0xFFF;
uint16_t current[] = {0, amplitude, 0, amplitude};
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*) ¤t[0], sizeof(uint16_t), DAC_ALIGN_12B_R);
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*) ¤t[1], sizeof(uint16_t), DAC_ALIGN_12B_R);
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*) ¤t[2], sizeof(uint16_t), DAC_ALIGN_12B_R);
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*) ¤t[3], sizeof(uint16_t), DAC_ALIGN_12B_R);
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_1);
HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_2);
HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_3);
I don't know how I can connect my timers to initiate the transfer of data from memory to the DAC. I hope you can help me give me a direction in the right way.
I have set up my timers so that they give a DMA request when the timer has expired.
While doing so I can create a kind-of biphasic pulse but only for the positive side. The parameters which are adjustable are the two pulse widths, the interphase interval and the period of this pulse. Note that the negative phase will be positive, so it will output two positive pulses.
You are configuring it wrong way.
Configure DAC trigger (this trigger will force DAC to request data transfer)
Configure DAC DMA
Configure timer to generate apripiate trigger
Related
I'm trying to figure out if there is a way of using any of the timers to generate interrupts at more than one delay time.
E.G. I want to be able to start a timer, then have it cause interrupts at say 20us, 50us, 100us, 300us.
I can see that its perfectly possibly (and easy) to have timers cause an interrupt when the end of the count has elapsed (using HAL) but having trouble working out if I can do what I want using only one timer.
I notice some timers are 4 channel but not sure if they can be set as required.
I guess my fall back is to use one timer for each (but ideally I would like to keep other timers in case they are needed for other tasks).
I've read the docs but having trouble understanding if the device can be configured as I want it to.
The easiest way - is to change the timer period (write a different value to the ARR register, use __HAL_TIM_SET_AUTORELOAD macro if you prefer HAL) in each interrupt. This way every other period could be different.
Just keep in mind ARR buffering, or Auto-reload preload, see ARPE bit decription in TIMx_CR1. if it is enabled - the new value of ARR will be taken in account only after the next update event.
You can have more than one interrupt generated during one cycle. You need to use output compare registers and enable their interrupts.
In the timer interrupt handler routine you will need to check what has triggered the interrupt.
There are multiple ways you can achieve it. One a little blunt and simple, another one slightly more complicated, but once set up, very effective and quick to process.
Approach number 1, a little limited, but also a little simpler: use capture/compare (in this case, compare specifically) to trigger an interrupt. Since timers have up to 4 capture/compare values, you can have up to 4 interrupts. You will have to check which exactly interrupt fired tho in the handler. Which specifically compare value, I mean.
Approach number 2, more flexible, more precise, but a bit more hassle to set up: master-slave timers. You can have one timer (including basic) be the clock source for the other timers ("tick" on the rising edge of master slave setting). For example, master timer ticks at 10kHz, and its slave timer uses its output as a clock source and ticks to, say, 50. Or 100. You can have multiple slaves. Depending on what timer's interrupt fires, you know right away how much time exactly passed, no need to check values in compare register or anything.
Mixed method: slave timers can have capture/compare too by the way, if you're into that. You can create huge timer chains if you want to.
You have a dozen timers in your MCU, you can probably spare 1 or 2 more for this purpose. Once you get it working, should make your life very easy.
This is how I set up a timer that is a slave, but also a master to another timer. TMR is TIM3 with TIM1 as a master:
/*
* CR1 reset value = 0x00000000
* ARR is not buffered
* upcounter (default)
* update request source: only overflow/underflow
*
* */
TMR->CR1 = 0x00; //reset
TMR->CR1 |= TIM_CR1_URS;
/*
* 108MHz Clock, 64000 prescaler -> 2kHz Timer Clock
* Reload value 6, Period = 6/2000s = 3ms
* */
TMR->PSC = (108000U / 2U) - 1U; //APB1 is 54MHz, timer is 2x APB1 Freq, so now the timer is at 2kHz; 16-bit value!!! 65535 max!
TMR->ARR = 6U - 1U; //6 ticks at 2kHz is 3ms
TMR->CNT = 0x00; //start value
TMR->EGR |= TIM_EGR_UG; //force update event and load all values into registers
TMR->SR &= ~TIM_SR_UIF; //force clear update event
/*
* SMCR Slave Mode Control Register reset value = 0x00000000
* Trigger Selection - ITR0 (TIM1 as Master for TIM3)
* Slave Mode Selection - Trigger Mode - The counter starts at a rising edge of the trigger TRGI (but it is not
* reset). Only the start of the counter is controlled (0b0110)
*/
TMR->SMCR = 0x00; //reset
TMR->SMCR |= (0x00 << TIM_SMCR_TS_Pos) | (0x00 << 16U) | (0x06 << TIM_SMCR_SMS_Pos);
/*
* CR2 reset value = 0x00000000
* Master Mode Selection 1: OC1REF Triggers TRGO to start another timer
* Master Mode Selection 2: reset
* Compare value: 4 (output LOW: CNT = 0, 1, 2, 3; output HIGH CNT = 4, 5)
* Duty cycle: 33.33%
* Output compare 1 mode - PWM mode 2 (0b0111)
*
* */
TMR->CR2 = 0x00; //reset
TMR->CR2 |= (0x04 << TIM_CR2_MMS_Pos); //OC1REF as TRGO
TMR->CCR1 = 4U;
TMR->CCMR1 = 0x00;
TMR->CCMR1 |= (0x07 << TIM_CCMR1_OC1M_Pos);
/*
* Capture Compare Enable Register
* Polarity: default - active high
* Capture Compare Output Enable
* */
TMR->CCER = 0x00; //reset
TMR->CCER |= TIM_CCER_CC1E;
The rest of my example/training project, including screenshots of waveform, are here: Github Chained Timers Demo. There you will find TIM1 to be the master, TIM3 its slave and the master of TIM5, and so on. Timers are connected in different master-slave modes.
I'm curious about the delay time between the title mentioned, I toggled an IO when I wrote data into UART->DR, the delay time varies from 3 micro seconds to 10x micro seconds
int main(void)
{
/* initial code generated by STMCubeMX */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
while (1)
{
HAL_Delay(50);
if (USART_GetFlagStatus(&huart1, USART_SR_TXE) == SET)
{
USART_SendData(&huart1, 'F');
}
}
}
void USART_SendData(UART_HandleTypeDef *huart, uint16_t Data)
{
assert_param(IS_USART_ALL_PERIPH(USARTx));
assert_param(IS_USART_DATA(Data));
GPIOB->BSRR = GPIO_PIN_1; // Tick an IO pin for debugging
GPIOB->BSRR = (uint32_t)GPIO_PIN_1 << 16u; // reset bit
huart->Instance->DR = (uint8_t)(Data & (uint8_t)0x00FF); // send data (write DR)
}
I'm not sure whether the time jitters is related with BAUD rate 9600(104 micro seconds/bit),
Isn't the data should be showed immediately when DR register written????
And why isn't the delay time all the same(or close)?
Isn't the data should be showed immediately when DR register written????
Not necessarily.
You are only showing us high-level language source code.
Have you looked at the actual instruction trace to determine the instruction time between these operations?
How do you ensure that no interrupt is serviced between these operations?
And why isn't the delay time all the same(or close)?
Apparently that depends on the design of the UART.
You report that the baudrate is 9600, and (as expected) the intervals for each bit appear to be slightly longer than 100microsec.
The fact that the observed latency is less than one bit interval is significant.
The typical UART uses a clock (aka the baudrate generator) that is 16 times faster than the configured baudrate.
This faster-than-necessary clock is needed to oversample the receiving signal, which can arrive at anytime, i.e. it's asynchronous communication after all.
For the transmit clock, the baudrate generator is divided down to the nominal baudrate.
So for transmission, that clock quantizes in time when each bit (of the frame) will start (and end) its transmission.
Since the write to the UART TxD data register is performed by the CPU, and that operation is not synchronized with the transmit clock, you should therefore expect a random delay of up to one bit interval before the start bit of the frame appears on the wire.
I am trying to implement my own SPI communication from FPGA to STM in which my FPGA serve as MASTER and generate Chip enable and clock for communication. FPGA transmit data at its rising edge and receive data at its falling edge my FPGA code works properly.
In STM side i capture this master clock on interrupts and receive data at its rising edge and transmit at its falling edge but communication not work properly if i increase clock speed from 250khz
According to my understand STM work at 168 Mega hz i set clock setting according to 168Mhz and handling of 1mhz interrupt is not a big problem so can you any guide how i handle this high speed clock in STM
My code is written below
/*
* Project name:
EXTI_interrupt (EXTI interrupt test)
* Copyright:
(c) Mikroelektronika, 2011.
* Revision History:
20111226:
- Initial release;
* Description:
This code demonstrates how to use External Interrupt on PD10.
PD10 is external interrupt pin for click1 socket.
receive data from mosi line in each rising edge.
* Test configuration:
MCU: STM32F407VG
http://www.st.com/st-web-
ui/static/active/en/resource/technical/document/datasheet/DM00037051.pdf
dev.board: EasyMX PRO for STM32
http://www.mikroe.com/easymx-pro/stm32/
Oscillator: HSI-PLL, 140.000MHz
Ext. Modules: -
SW: mikroC PRO for ARM
http://www.mikroe.com/mikroc/arm/
* NOTES:
receive 32 bit data from mosi line in each rising edge
*/
//D10 clk
//D2 ss
//C0 MOSI
//C1 FLAG
int read=0;
int flag_int=0;
int val=0;
int rec_data[32];
int index_rec=0;
int display_index=0;
int flag_dint=0;
void ExtInt() iv IVT_INT_EXTI15_10 ics ICS_AUTO {
EXTI_PR.B10 = 1; // clear flag
flag_int=1; //Flag on interrupt
}
TFT_Init_ILI9340();
void main() {
GPIO_Digital_Input(&GPIOD_BASE, _GPIO_PINMASK_10);
GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_13); // Set PORTD as
digital output
GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_12); // Set PORTD as
digital output
GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_14); // Set PORTD as
digital output
GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_15); // Set PORTD as
digital output
GPIO_Digital_Input(&GPIOA_IDR, _GPIO_PINMASK_0); // Set PA0 as
digital input
GPIO_Digital_Input(&GPIOC_IDR, _GPIO_PINMASK_0); // Set PA0 as
digital input
GPIO_Digital_Input(&GPIOC_IDR, _GPIO_PINMASK_2); // Set PA0 as
digital input
GPIO_Digital_Output(&GPIOC_IDR, _GPIO_PINMASK_1); // Set PA0 as
digital input
//interupt register
SYSCFGEN_bit = 1; // Enable clock for alternate pin
functions
SYSCFG_EXTICR3 = 0x00000300; // Map external interrupt on PD10
EXTI_RTSR = 0x00000000; // Set interrupt on Rising edge
(none)
EXTI_FTSR = 0x00000400; // Set Interrupt on Falling edge
(PD10)
EXTI_IMR |= 0x00000400; // Set mask
//NVIC_IntEnable(IVT_INT_EXTI15_10); // Enable External interrupt
while(1)
{
//interrupt is not enable until i push the button
if((GPIOD_ODR.B2==0)&&(flag_dint==0))
{ if (Button(&GPIOA_IDR, 0, 1, 1))
{
Delay_ms(100);
GPIOC_ODR.B1=1; //Status for FPGA
NVIC_IntEnable(IVT_INT_EXTI15_10); // Enable External interrupt
}
}
if(flag_int==1)
{
//functionality on rising edge
flag_int=0;
if(index_rec<31)
{
//display data on led
GPIOD_ODR.B13= GPIOC_IDR.B0;
//save data in an array
rec_data[index_rec]= GPIOC_IDR.B0;
//read data
index_rec=index_rec+1;
}
else
{
flag_dint=1;
NVIC_IntDisable(IVT_INT_EXTI15_10);
}
} // Infinite loop
}
}
Without getting into your code specific, see PeterJ_01's comment, the clock rate problem can be explained by a misunderstanding of throughput in your assumtions.
You assume that given that your STM device has a clock of 168Mhz it can sustain the same throughput of interrupts, which you seem to have conservatively relaxed to 1Mhz.
However the throughput of interrupts it will be able to support is given by the inverse of the time it takes the device to process each interrupt. This time includes both the time the processor takes to enter the service routing (ie detect the interrupt, interrupt the current code and resolve from the vector table where to jump to) plus the time taken to execute the service routine.
Lets be super optimistic and say that entering the routine takes 1 cycle and the routing itself takes 3 (2 for the flags you set and 1 for the jump out of the routine). This gives 4 cycles at 168Mhz is 23.81ns, taking the inverse 42Mhz. This can also be computed by dividing the maximum frequency you would achieve (168Mhz) by the number of cycles spent processing.
Hence our really optimistic bound is 42Mhz, but realistically will be lower. For a more accurate estimate you should test your implementation timings and dig into your device's documentation to see interrupt response times.
How to get accurate milliseconds?
I need to calculate the delay of sending data from Arduino A to Arduino B. I tried to use DS3231 but I cannot get milliseconds. What should I do to get accurate milliseconds from DS3231?
The comment above is correct, but using millis() when you have a dedicated realtime clock makes no sense. I'll provide you with better instructions.
First thing in any hardware interfacing project is a close reading of the datasheet. The DS3231 datasheeet reveals that there are five possible frequencies of sub-second outputs (see page 13):
32 KHz
1 KHz
1.024 KHz
4.096 KHz
8.192 KHz
These last four options are achieved by various combinations of the RS1 and RS2 control bits.
So, for example, to get exact milliseconds, you'd target option 2, 1KHz. You set RS1 = 0 and RS2 = 0 (see page 13 of the datasheet you provided) and INTCN = 0 (page 9). Then you'd need an ISR to capture interrupts from the !INT/SQW pin of the device to a digital input pin on your Arduino.
volatile uint16_t milliseconds; // volatile important here since we're changing this variable inside an interrupt service routine:
ISR(INT0_vect) // or whatever pin/interrupt you choose
{
++milliseconds;
if(milliseconds == 999) // roll over to zero
milliseconds = 0;
}
OR:
const int RTCpin = 3; // use any digital pin you need.
void setup()
{
pinmode(RTCpin, INPUT);
// Global Enable INT0 interrupt
GICR |= ( 1 < < INT0);
// Signal change triggers interrupt
MCUCR |= ( 1 << ISC00);
MCUCR |= ( 0 << ISC01);
}
If these commands in setup() don't work on your Arduino, google 'Arduino external interrupt INT0'. I've shown you two ways, one with Arduino code and one in C.
Once you have this ISR working and pin3 of the DS3231 connected to a digital input pin of your choosing, that pin will be activated at 1KHz, or every millisecond. Perfect!
// down in main program now you have access to milliseconds, you might want to start off by setting:
// When 1-second RTC changes seconds:
milliseconds = 0; // So you can measure milliseconds since last second.
That's all there is to it. All you need to learn now is how to set the command register using I2C commands and you're all set.
The C code example gains 1ms every second. Should be:
{
if (milliseconds == 999) // roll over to zero
milliseconds = 0;
else
++milliseconds;
}
I am programming the microcontroller PIC16F676 SPI interface with MCP2515. It will set a flag in every 224ms, and timercounter will increase from 0*F8 to 0*FF then overflow to set this flag. Therefore, 32ms * 07H = 224ms. The question is how to let the timer interrupt every 32ms,WHERE this 32ms comes from.
//Timer interrupts every 32ms and set a flag every 224ms (32ms * 07H = 224ms)
//Initial value = FFH - 07H = F8H
if(T0IF) //TMR0 overflow interrupt flag bit
{
TimerCounter++;
if(!TimerCounter) //if rolled over, set flag. Main will handle the rest.
{
TimerCounter = 0*F8;
gSampleFlag = 1;
}
T0IF = 0; //reset for next flag
}
The 32 ms period of the timer is determined by the configuration of the timer, which is not included in your code excerpt (i.e., it may be done elsewhere in your code). Read section 4.0 of the PIC16F630/676 datasheet, which explains the TIMER0 Module.
Timer0 is configured as follows:
T0CS is either:
cleared to select the internal clock source (Timer mode) for Timer0,
or set to select the external clock source (Counter mode).
T0IE is set to enable the Timer0 interrupt.
The prescaler may be used in conjunction with the internal clock source to adjust the tick rate of Timer0
PSA is cleared to assign the prescaler to Timer0.
PS2:PS0 are set to select the prescaler rate.
So either the external clock source or the internal clock source and prescaler determine the tick rate of Timer0.
32ms is the time period of clock source which your timer counter is counting for 0x07 times.
Your timer unit is synchronised with common clock source which is derived from either external crystal or internal oscillator. During clock configuration you need to decide what should be peripheral bus clock through which your timer unit is interfaced. While configuring timer unit you can further divide this peripheral clock to less frequency using prescaler to increase the range of period.
Now suppose your peripheral bus clock frequency is 1MHz and your prescaler is 1, your counter will increment or decrement every 1us and for 0x07 count, it will generate only 7us of period. In your example you need to set prescaler 32000 (if it allowed to set) as reference clock for counting so that 1 count means 32ms.