Author Topic: Starting with STM32 (NUCLEO-L412KB)  (Read 12544 times)

0 Members and 1 Guest are viewing this topic.

Online gf

  • Super Contributor
  • ***
  • Posts: 1302
  • Country: de
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #125 on: May 21, 2024, 09:34:12 pm »
Input buffer = 1000 samples
Output buffer = 100 samples
process time = 936us @ 80MHz with STM32L412KB

Yet another test:
Process time: 836us (less)

Both are about 9.5 cycles per output sample per tap.

@Picuino, could you please also check how fast arm_fir_decimate_q31() runs with 6 taps and decimation factor 2, with 1000 samples input and 500 samples output?
taps = [ -97330866, 172200328, 998872362, 998872362, 172200328, -97330866 ].

And for comparison, this one would be interesting, too: https://godbolt.org/z/G3Gr1r5xc
I wonder if this hard-coded special case can beat the generic implementation. In fact, I'm not sure if it really can.

EDIT: A sinc3 filter with 4 taps seems to be fine for the first stage, too: https://godbolt.org/z/bP9WrdGqq Should be even faster.

The first decimation stage is certainly the most time-critical one. Subsequent stages run at a lower sample rate. They need to process fewer samples per time unit, and the lower sample rate also leads to fewer taps than required for a single-stage decimator. Decimate-by-2 stages need the smallest number of taps, so I guess it might be beneficial to cascade multiple divide-by-2 stages.



Quote
I have no idea about the speed of the other processor (STM32G431KB). In principle it has more clock speed and also has instructions to accelerate the digital filters.

What instructions does it have that the STM32L4 does not have? Aren't both Cortex M4? I may be wrong, but my understanding is that the STM32L4 also supports the Cortex M4 DSP instruction set.

EDIT:

Quote from: datasheet
The STM32L412xx devices are ultra-low-power microcontrollers based on the high-performance Arm® Cortex®-M4 32-bit RISC core operating at a frequency of up to 80 MHz. The Cortex-M4 core features a Floating point unit (FPU) single precision that supports all Arm® single-precision data-processing instructions and data types. It also implements a full set of DSP instructions...
« Last Edit: May 22, 2024, 02:08:50 pm by gf »
 

Offline dietert1

  • Super Contributor
  • ***
  • Posts: 2235
  • Country: br
    • CADT Homepage
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #126 on: May 22, 2024, 05:44:37 am »
To start with the STM32G4xx runs at about twice the processor clock (6 nsec cycle).

It implements an additional DSP unit they call FMAC that includes a local memory for an array of filter coefficients and address generation logic to speed up FIR calculation. It works independent of the M4 core, similar to DMA. It implements a 16 x 16 -> 26 bit MAC. That means it will probably do about 10 or 20 taps within 300 nsec.
They demonstrate HAL support for the FMAC unit in their AN 5305.

Regards, Dieter
« Last Edit: May 22, 2024, 06:55:53 am by dietert1 »
 
The following users thanked this post: gf

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-G431KB)
« Reply #127 on: May 22, 2024, 02:17:59 pm »
I thought I wouldn't get the order from Mouser until next week, but it just got to me, so I'm going to set up the STM32G431KB to test directly with this other microcontroller.

Don't forget to join jumper 17 to have the external clock available!!
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-G431KB)
« Reply #128 on: May 22, 2024, 02:48:18 pm »
On this board it is necessary to link jumper SB13 so that the external clock comes from the STLINK.
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #129 on: May 22, 2024, 04:07:14 pm »
I had to update the firmware of the STLINK integrated in the development board.

Apparently it consumes quite a lot of current (or for some other reason I don't know) and I was not able to update through the USB HUB I have on my desk.

I had to directly connect the NUCLEO-G431KB board with a short USB cable to the computer to be able to do the firmware update.
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #130 on: May 22, 2024, 04:27:56 pm »
I have made the update with the option:
MCO = 1/5 (5MHz).
Because the STLINK has a frequency of 25MHz and that way it sends 5MHz through jumper SB13 to the PF0-OSC-IN input of the STM32G431KB microcontroller.

I am still having connection problems through the cable connected to the USB HUB
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #131 on: May 22, 2024, 04:40:31 pm »
Fortunately, I found a long (150cm) microUSB cable with which, at last, the development board works.

The STM32G431KB definitely does not like connections to the computer via a USB HUB.

The maximum consumption that I have been able to measure is 38mA for the whole board (STLINK + STM32) running at full speed. It doesn't seem too much current, but, for some reason, it didn't work before.
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #132 on: May 22, 2024, 05:05:06 pm »
@Picuino, could you please also check how fast arm_fir_decimate_q31() runs with 6 taps and decimation factor 2, with 1000 samples input and 500 samples output?
taps = [ -97330866, 172200328, 998872362, 998872362, 172200328, -97330866 ].

Well, STM32G431KB running at 170MHz, optimization -O3, filtering 1000 samples with decimation at 500 samples output.
Slow function arm_fir_decimate_q31().
Running time: 318us

EDIT:
Fast function arm_fir_decimate_fast_q31().
Running time: 354us

It is not very logical. I'm going to try optimizing for speed.
« Last Edit: May 22, 2024, 05:08:14 pm by Picuino »
 
The following users thanked this post: gf

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #133 on: May 22, 2024, 05:09:42 pm »
Optimizing for speed -Ofast, the fast function maintains a long execution time (354us).
And the slow function maintains a short runtime (318us).
« Last Edit: May 22, 2024, 05:12:08 pm by Picuino »
 

Offline Tation

  • Regular Contributor
  • *
  • Posts: 68
  • Country: pt
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #134 on: May 22, 2024, 05:46:28 pm »
The maximum consumption that I have been able to measure is 38mA for the whole board (STLINK + STM32) running at full speed. It doesn't seem too much current, but, for some reason, it didn't work before.

Nucleo boards do have a hand-removable jumper on the underside (near the reset button) allowing you to measure current consumption for the MCU without the ST-link stuff.
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #135 on: May 22, 2024, 08:35:42 pm »
Yes, I know it. I just wanted to measure the total current to check if there was any consumption problem due to which the USB cable that I used to use before without problem and now does not work with this new board.



Changing the subject, I am configuring the TIMER2 for PWM output and everything seems to work fine, except the period which is little bit larger than expected.
The clock is 170 MHz, so I have set the prescaler to 170, the period to 1000 and the duty cycle to 500.

The period is 50% as I expected, but it is worth 1008us, 8us more than expected.
I have done checks and the micro is working with external clock which has a frequency of 4.99986MHz on my oscilloscope.
I can't explain this discrepancy.
« Last Edit: May 23, 2024, 07:47:28 am by Picuino »
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #136 on: May 22, 2024, 08:50:44 pm »
Solution:
Prescaler by 169 so that it divides by 170.
Counter period in 999 to divide by 1000.

Now, PWM output has 999.972 Hz
 

Online gf

  • Super Contributor
  • ***
  • Posts: 1302
  • Country: de
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #137 on: May 22, 2024, 10:07:50 pm »
@Picuino, could you please also check how fast arm_fir_decimate_q31() runs with 6 taps and decimation factor 2, with 1000 samples input and 500 samples output?
taps = [ -97330866, 172200328, 998872362, 998872362, 172200328, -97330866 ].

Well, STM32G431KB running at 170MHz, optimization -O3, filtering 1000 samples with decimation at 500 samples output.
Slow function arm_fir_decimate_q31().
Running time: 318us

EDIT:
Fast function arm_fir_decimate_fast_q31().
Running time: 354us

It is not very logical. I'm going to try optimizing for speed.

That's a bit disappointing for the 2x faster µC :(

Could you also try to copy my function into your program and test the same? https://godbolt.org/z/bP9WrdGqq
It's hardcoded for 4 taps and 2x decimation - no unnecessary generic sctuff. If it also does not run faster then :-//
 

Offline dietert1

  • Super Contributor
  • ***
  • Posts: 2235
  • Country: br
    • CADT Homepage
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #138 on: May 23, 2024, 03:57:19 am »
If you look into the CubeMX clock setup page, you will likely see that flash memory clock is less than CPU clock, maybe a factor two. In order to get that factor two you may have to run code from RAM. On the first page of the G4 datasheet they mention a special 32K block "CCM SRAM" to be used as routine booster.
There is an application note AN4296 on how to get this going.

Regards, Dieter
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #139 on: May 23, 2024, 07:40:59 am »
YES, in fact I had already tested that functionality to turn a pin on and off very fast.
https://www.eevblog.com/forum/microcontrollers/starting-with-stm32-(nucleo-l412kb)/msg5499019/#msg5499019

Thanks, I will try with RAM execution.
 

Online gf

  • Super Contributor
  • ***
  • Posts: 1302
  • Country: de
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #140 on: May 23, 2024, 08:27:45 am »
If you look into the CubeMX clock setup page, you will likely see that flash memory clock is less than CPU clock, maybe a factor two.

I did take a closer look to the datasheet and the reference manual (RM0440).

The datasheet claims

Quote from: datasheet
Arm ® 32-bit Cortex®-M4 CPU with FPU, Adaptive real-time accelerator (ART Accelerator) allowing 0-wait-state execution from Flash memory, frequency up to 170 MHz with 213 DMIPS

According to the reference manual, 4 wait states (= 5 CPU cycles latency) must be programmed for the flash @170MHz. OTOH, the width of the G431 flash is 64 bits, so four 16-bit instructions can be fetched with one access.

In order to avoid a pipeline stall due to wait states every 4 instructions, prefetch must be enabled (figure 9 in RM0440). Depending on the executed instruction mix I guess it may come close to the promised "0-wait-state execution" if prefetch and I-cache are enabled. The reference manual sais that I-cache is enabled by default, but prefetch is off by default.

EDIT:

Found these slides which give more insight into how the ART Accelerator works:
https://www.st.com/resource/en/product_training/STM32G4-Memory-Flash_FLASH.pdf

My conclusion is that small loops (which fit entirely into the I-cache) can benefit from I-cache alone (except for the first loop pass), while sequential code requires prefetch in order to mitigate the wait states. When prefetch is enabled, the I-cache degenerates mostly to a cache for instructions at branch targets.

What I still do not understand:
The slides say that 7 wait states are required @170Mhz, while RM0440 only ask for 4 wait states @170MHz?
« Last Edit: May 23, 2024, 11:11:27 am by gf »
 
The following users thanked this post: gpr

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #141 on: May 23, 2024, 01:43:48 pm »
Input buffer = 1000 samples
Output buffer = 500 samples
FIR coefficients = 6

arm_fir_decimate_q31() running in RAM: 318us

arm_fir_decimate_fast_q31() running in RAM: 354us

Exactly the same times as running in flash.
 
The following users thanked this post: gf

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #142 on: May 23, 2024, 01:52:19 pm »
decimate_2_sinc3() with 4 coefficients, running in flash: 80us
 
The following users thanked this post: gf

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #143 on: May 23, 2024, 01:55:52 pm »
I am going to start up the milliohm meter that I already had built, with the STM32. I will have to be careful with the voltages (5v for the milliohm meter and 3.3V for the microcontroller).
This will allow me to get the STM32 working before designing and assembling a specific stage for it with the INA826 and the OPA2325.

I need to start up:
  1. Two ADC @ 2.833MHZ synchronized to sample at the same time.
  2. DMA for 2 ADC
  3. One DAC for output reference, with DMA.
  4. One DAC for output signal, with DMA and synchronized with ADC.
  5. A digital FIR filter with decimation.

EDIT:
When I finish getting all the STM32 peripherals up and running, I will continue the thread on the main project: building a Homebrew Lock-In amplifier.
I hope it is not too confusing. I have preferred to separate it because it is one thing to learn how to program the STM32 and another thing what I am going to do with it.
« Last Edit: May 23, 2024, 04:18:38 pm by Picuino »
 
The following users thanked this post: mskeete

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #144 on: May 23, 2024, 07:49:20 pm »
I can't get the DAC to extract values from the buffer with DMA.
I have only been able to get it to output individual values without DMA.
ChatGPT does not help either.

dac.c:
Code: [Select]
#include <ctype.h>
#include "main.h"
#include "dac.h"

uint16_t dac1_buff[DAC1_BUFF_SIZE];
uint16_t dac2_buff[DAC2_BUFF_SIZE];

void dac1_dma_init(void) {
    /*
     * DAC Initialization
     */

    // Configure PA4 for analog output
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);


    // Configure DAC1
    __HAL_RCC_DAC1_CLK_ENABLE();

    DAC_HandleTypeDef hdac1;
    hdac1.Instance = DAC1;
    if (HAL_DAC_Init(&hdac1) != HAL_OK) {
        dac_Error_Handler();
    }

    /*
     *  DAC channel OUT1 config
     */
    DAC_ChannelConfTypeDef sConfig = { 0 };
    sConfig.DAC_HighFrequency = DAC_HIGH_FREQUENCY_INTERFACE_MODE_AUTOMATIC;
    sConfig.DAC_DMADoubleDataMode = DISABLE;
    sConfig.DAC_SignedFormat = DISABLE;
    sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
    sConfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
    sConfig.DAC_Trigger2 = DAC_TRIGGER_NONE;
    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
    sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_EXTERNAL;
    sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
    HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1);

    // Enable the DAC channel
    HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);

    // Configure DMA1 Channel 3
    DMA_HandleTypeDef hdma1_dac1;
    __HAL_RCC_DMA1_CLK_ENABLE();

    hdma1_dac1.Instance = DMA1_Channel3;
    hdma1_dac1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma1_dac1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma1_dac1.Init.MemInc = DMA_MINC_ENABLE;
    hdma1_dac1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma1_dac1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma1_dac1.Init.Mode = DMA_CIRCULAR;
    hdma1_dac1.Init.Priority = DMA_PRIORITY_HIGH;

    HAL_DMA_Init(&hdma1_dac1);


    // Enable interrupts of full transfer and half transfer
    __HAL_DMA_ENABLE_IT(&hdma1_dac1, DMA_IT_TC | DMA_IT_HT);

    // Link DMA to DAC
    __HAL_LINKDMA(&hdac1, DMA_Handle1, hdma1_dac1);

    // Configure NVIC for DMA
    HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);

    HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048);

    // Start DAC with DMA
    HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*) dac1_buff,
    DAC1_BUFF_SIZE * sizeof(uint16_t) / sizeof(uint32_t),
    DAC_ALIGN_12B_R);
}

void DMA1_Channel3_IRQHandler(void) {
    // Half transfer
    if (DMA1->ISR & DMA_ISR_HTIF3) {
        DMA1->IFCR = DMA_IFCR_CHTIF3;

    }

    // Full transfer
    if (DMA1->ISR & DMA_ISR_TCIF3) {
        DMA1->IFCR = DMA_IFCR_CTCIF3;

    }
}

void dac_Error_Handler(void) {

}


timer.c:
Code: [Select]
#include <ctype.h>
#include "timer.h"
#include "stm32g4xx_hal.h"

void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
void timer_Error_Handler(void);

void timer2_init(void) {
    TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
    TIM_MasterConfigTypeDef sMasterConfig = { 0 };
    TIM_OC_InitTypeDef sConfigOC = { 0 };

    // Initialize TIM2 instance
    TIM_HandleTypeDef htim2;
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 17 - 1;  // Set prescaler. Divide by 17
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 10 - 1;  // Set period to 10
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

    // Base initialization
    if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
        timer_Error_Handler();
    }

    // Configure clock source
    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
        timer_Error_Handler();
    }

    // Initialize PWM
    if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) {
        timer_Error_Handler();
    }

    // Configure master synchronization
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig)
            != HAL_OK) {
        timer_Error_Handler();
    }

    // Configure PWM channel
    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 5;  // Set pulse width to 5
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4)
            != HAL_OK) {
        timer_Error_Handler();
    }

    // Post-initialize TIM2 (configure GPIO)
    HAL_TIM_MspPostInit(&htim2);

    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
}

void timer_Error_Handler(void) {

}

Timer2 is running with PWM output at PA10.
 

Offline dietert1

  • Super Contributor
  • ***
  • Posts: 2235
  • Country: br
    • CADT Homepage
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #145 on: May 23, 2024, 08:18:47 pm »
No error condition?
Why this expression: DAC1_BUFF_SIZE * sizeof(uint16_t) / sizeof(uint32_t)
in HAL_DAC_Start_DMA(&hdac1,...
Shouldn't this be the number of DMA cycles = DAC1_BUFF_SIZE?
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #146 on: May 23, 2024, 08:27:43 pm »
Changed. The DAC output remains at 0 Volts.

Does anyone have an example of a DAC working with DMA that I can adapt?
 

Offline dietert1

  • Super Contributor
  • ***
  • Posts: 2235
  • Country: br
    • CADT Homepage
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #147 on: May 24, 2024, 05:33:25 am »
The G4xx repository provides 16 examples, among them DMA and waveform generation. I noticed that they use Timer 6 for DAC_Trigger2.
Another description is here: https://www.theengineeringprojects.com/2021/12/using-dac-with-stm32.html
They also use Timer 6. It seems to be for F303.
Last year i ordered some STM32G474RCT in order to have a look at their high resolution timer. The chips were mounted to SMD adapters and are waiting for a test..

Regards, Dieter
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #148 on: May 24, 2024, 08:09:19 am »
Thank you.
I'm going to start a Cube MX project from scratch to get the DAC up and running and from there I'll take the code to the other project where I'll put everything together.
 

Offline PicuinoTopic starter

  • Super Contributor
  • ***
  • Posts: 1019
  • Country: es
    • Picuino web
Re: Starting with STM32 (NUCLEO-L412KB)
« Reply #149 on: May 24, 2024, 11:19:26 am »
The detailed reference manual is also a good source of details for programming the microcontroller.
It lists all possible synchronization sources and which register to program them with.
This CubeMX and HAL libraries thing is giving me more problems in the end than looking for detailed information of each register and programming them directly.
Since I don't have to make programs for many different micros, portability doesn't matter so much to me.

https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
« Last Edit: May 24, 2024, 12:15:29 pm by Picuino »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf