آموزش STM32 با توابع LL قسمت بیست و چهارم: ADC با سرعت دو برابر!

0
35
آموزش STM32 با توابع LL قسمت بیست و چهارم: ADC با سرعت دو برابر!
آموزش STM32 با توابع LL قسمت بیست و چهارم: ADC با سرعت دو برابر!

در بخش دهم، با واحد ADC آشنا شدیم و در بخش پانزدهم نحوه دریافت اطلاعات از ADC توسط DMA را دیدیم. در این بخش می‌خواهیم کاری کنم که واحد ADC با سرعت بیشتری نمونه‌برداری کند. میکروکنترلر STM32F103C8 دارای دو واحد ADC است که حداکثر سرعت نمونه‌برداری هرکدام از آن‌ها 1 Ms/s یا یک‌میلیون نمونه در ثانیه است. ما می‌توانیم با به‌کار بردن همزمان این دو ADC دو برابر یعنی 2 Ms/s دست‌یابیم. کاربرد این سرعت نمونه‌برداری برای سیگنال‌هایی است که فرکانس آن‌ها بیش از  500KHz است. در این حالت با توجه به نرخ نایکویست، فرکانس نمونه‌برداری که باید حداقل دو برابر فرکانس سیگنال باشد، بیش از 1 Ms/s می‌شود که با یک ADC امکان‌پذیر نیست.

حالت Dual fast interleaved
حالت Dual fast interleaved

ایجاد پروژه

بدین منظور باید در هنگام ایجاد پروژه بعد از تنظیم کلاک و دیباگ، کانال 0 مربوط به هر دو واحد ADC1 و ADC2 را فعال کنیم و سپس ADC1 را روی حالت Dual fast interleaved mode only تنظیم کنیم. بدین طریق ADC2 نیز در همین حالت قرار می‌گیرد. همچنین باید برای هر دو واحد ADC حالت Continuous Conversion mode را فعال کنیم و زمان نمونه‌برداری را 1.5 سیکل قرار دهیم. در آخر DMA را برای واحد ADC1 فعال می‌کنیم:

ایجاد پروژه
تنظیم ADC1.
تنظیم DMA واحد ADC1.
تنظیم DMA واحد ADC1.
 تنظیم واحد ADC2.
تنظیم واحد ADC2.
 تنظیم تایمر1.
تنظیم تایمر1.

تایمر 2 را نیز برای شمارش تعداد پالس کلاکی‌ که انجام نمونه‌گیری طول خواهد کشید، فعال می‌کنیم:

تنظیم تایمر2
تنظیم تایمر2

در آخر واحد USART3 را مشابه قبل برای فرستادن اطلاعات و چک کردن عملکرد پروژه فعال می‌کنیم. کلاک میکرو را روی 72MHz تنظیم کرده و کد پروژه را مانند قبل ایجاد می‌کنیم.

 

نوشتن کد پروژه

در ابتدای کد باید کتابخانه stdio را برای نمایش اطلاعات توسط printf اضافه کنیم و تابع‌هایی که از قبل تعریف کرده بودیم را به برنامه اضافه کنیم و این بار عمل ریتارگت را با USART3 انجام دهیم. مرحله مربوط به Project, Manage, Run-Time Environment را نیز باید در اینجا تکرار کنیم:

#include "stdio.h"

 

 /* Function for transmitting 8bit data via USART */
void write_uart(char data) 
{
while(!LL_USART_IsActiveFlag_TXE(USART3));
LL_USART_TransmitData8(USART3, (uint8_t)data);
}

/* Retargeting stdout_putchar as to use USART_TX for data output */ 
int stdout_putchar (int ch) {
write_uart(ch);
return (ch);
}

بافر حافظه مورد نیاز را تعریف می‌کنیم:

#define ARRAYSIZE (uint32_t) 100

uint32_t adc_read[ARRAYSIZE];

اکنون تنظیم و راه‌اندازی ADC ها و DMA را انجام می‌دهیم:

////ADC configuration

LL_ADC_Enable(ADC1);

LL_ADC_StartCalibration(ADC1); //Calibration start
while(LL_ADC_IsCalibrationOnGoing(ADC1)) //Calibration Done?
__NOP();

LL_ADC_REG_StartConversionSWStart(ADC1);

LL_ADC_Enable(ADC2);

LL_ADC_StartCalibration(ADC2); //Calibration start
while(LL_ADC_IsCalibrationOnGoing(ADC2)) //Calibration Done?
__NOP();

LL_ADC_REG_StartConversionSWStart(ADC2);
 ////DMA Configuration

LL_DMA_ConfigAddresses(DMA1,
LL_DMA_CHANNEL_1,
LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA),
(uint32_t)adc_read, //Address of the first element of the memory buffer
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

LL_DMA_SetDataLength(DMA1,
LL_DMA_CHANNEL_1,
ARRAYSIZE); 

LL_DMA_EnableIT_TC(DMA1,
LL_DMA_CHANNEL_1);

LL_DMA_EnableChannel(DMA1,
LL_DMA_CHANNEL_1);

 

در کد بالا تنها آدرس رجیستر ADC1 به‌عنوان مبدأ اطلاعات تعیین‌شده است، زیرا با تنظیم ADC ها برای کار در حالت Dual مقدار تبدیل‌شده سپس باید تایمر 1 را که در حالت PWM تنظیم کرده بودیم، راه‌اندازی کنیم و همچنین تایمر 2 را برای شمارش فعال کنیم:

////Timers configuration
LL_TIM_EnableCounter(TIM1); //Enable Timer1's counter
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1); //Enable channel1 of Timer1(PWM)
LL_TIM_EnableAllOutputs(TIM1); //Enable Timer1's outputs
LL_TIM_OC_SetCompareCH1(TIM1, (LL_TIM_GetAutoReload(TIM1) + 1) / 2); //Set Duty cycle

LL_TIM_EnableCounter(TIM2);

 

حالا در فایل stm32f1xx_it.c و در تابع وقفه DMA1 کد زیر را برای چاپ تعداد پالس‌های زمان تبدیل می‌نویسیم (در این فایل نیز باید stdio.h را اضافه کنیم) :

 /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
uint32_t count; 

if(LL_DMA_IsActiveFlag_TC1(DMA1) == 1)
{
LL_DMA_ClearFlag_TC1(DMA1);
count = LL_TIM_GetCounter(TIM2);
printf("count is %d\r\n", count);
LL_TIM_SetCounter(TIM2, 0);
}
/* USER CODE END DMA1_Channel1_IRQn 0 */

نتیجه در ترمینال سریال قابل مشاهده است؛

 

اکنون برای مقایسه نحوه عملکرد دو واحد ADC به‌صورت همزمان (با سرعت دو برابر) و یک ADC، به بخش دیباگ می‌رویم و بافر حافظه را به Watch1 اضافه می‌کنیم و سپس برنامه را اجرا می‌کنیم.

مقایسه نحوه عملکرد دو واحد ADC به‌صورت همزمان

بعد از اجرا کردن برنامه می‌بینیم که بافر به‌صورت زیر پرشده است:

بافر

می‌بینیم که نتیجه تبدیل ADC های 1 و 2 در 16 کم‌ارزش و 16 بیت پرارزش هر خانه از بافر قرارگرفته‌اند. برای حالتی که تنها یک ADC عمل نمونه‌برداری و تبدیل را انجام دهد، می‌توانیم از Cube MX یکی از واحدهای ADC را غیرفعال کنیم یا اینکه در کد برنامه، قسمت مربوط به فعال‌سازی ADC2 را کامنت کنیم. پس از اجرای کد در دیباگ داریم:

همان‌طور که می‌بینیم در این حالت نصف حالت قبلی نمونه‌برداری انجام‌شده است.

برای رسم شکل موج اطلاعات دریافت شده، می‌توانیم، کدهای printf قبلی را کامنت کنیم و کد زیر را برای حالت Dual به main.c اضافه کنیم:

 /* send array through usart_tx to serial plotter */
for(int i = 0; i < ARRAYSIZE; i++)
{
printf("%d\r\n", 0x0000FFFF & adc_read[i]);
printf("%6.0f\r\n", (float)((0xFFFF0000 & adc_read[i]) / 65535));
}

برای دیدن شکل موج می‌توانیم از ابزار Serial Plotter در نرم‌افزار Arduino IDE استفاده کنیم؛

ابزار Serial Plotter.
ابزار Serial Plotter.
شکل موج اطلاعات دریافت شده توسط ADC

در بخش بعدی در مورد ارتباط I2C صحبت خواهیم کرد.

 

   لینک این پروژه در گیت‌هاب

 

 

منبع:سیسوگ

برای این مقاله نظر بگذارید:

لطفا دیدگاه خود را بنویسید
لطفا نام خود را وارد کنید