برای کنترل مبدل دیجیتال به آنالوگ MCP4922 که یک تراشه دو کاناله ۱۲ بیتی است، با حداکثر سرعت در رزبری پای، بهترین رویکرد استفاده از درایور سختافزاری SPI لینوکس (spidev) است، دقیقاً مانند تراشه قبلی.
تراشه MCP4922 طبق دیتاشیت، میتواند با سرعت کلاک تا ۲۰ مگاهرتز (20 MHz) کار کند. ما از این سرعت برای دستیابی به حداکثر کارایی استفاده میکنیم.
۱. پیشنیازها و اتصالات سختافزاری
الف) فعالسازی SPI
مطمئن شوید که SPI در رزبری پای شما فعال است (sudo raspi-config -> Interface Options -> SPI -> Enable).
ب) اتصالات MCP4922 به RPi 3
MCP4922 از طریق پینهای سختافزاری SPI0 به رزبری پای متصل میشود:
| پایه MCP4922 | نام پایه | اتصال به RPi (BCM) | پین فیزیکی RPi |
| 9 | SDI (Data In) | MOSI (GPIO10) | Pin 19 |
| 8 | SCK (Clock) | SCLK (GPIO11) | Pin 23 |
| 7 | $\overline{\text{CS}}$ (Chip Select) | CE0 (GPIO8) | Pin 24 |
| 1 | VDD | 3.3V | Pin 17 |
| 4 | VSS | GND | Pin 6 |
| 10 | $\overline{\text{LDAC}}$ | GND (برای بهروزرسانی فوری) | Pin 9/GND |
| 11 | $\overline{\text{SHDN}}$ | VDD/3.3V (برای فعال بودن دائم) | Pin 1 |
نکته سرعت: برای حداکثر سرعت، پین $\overline{\text{LDAC}}$ (Load DAC) را به زمین (GND) وصل کنید تا هر دادهای که ارسال میشود، بلافاصله روی خروجی DAC (VOUT) ظاهر شود.
۲. کد برنامه C (با حداکثر سرعت)
این برنامه یک موج پلهای (Step Wave) یا موج دندانارهای (Sawtooth) ساده را با سرعت بالا روی کانال A تولید میکند. ما از printf داخل حلقه اصلی پرهیز میکنیم تا عملیات ورودی/خروجی سیستم عامل (IO) سرعت را کاهش ندهد.
یک فایل جدید به نام dac_spi.c ایجاد کنید:
C
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
// مسیر دستگاه SPI (معمولاً CE0)
static const char *device = "/dev/spidev0.0";
// حداکثر سرعت کلاک برای MCP4922
// 20,000,000 Hz = 20 MHz (باید مطمئن شوید سیمکشی خوب است)
static uint32_t speed = 20000000;
// تنظیمات SPI
static uint8_t mode = SPI_MODE_0; // MCP4922 از Mode 0 یا 3 استفاده میکند
static uint8_t bits = 8;
static uint16_t delay = 0;
// تابع ساخت 16 بیت داده برای MCP4922
// control_word = D15 D14 D13 D12 (4 بیت کنترل)
// value = D11...D0 (12 بیت داده)
uint16_t build_command(uint8_t channel, uint16_t value) {
uint16_t command = 0;
// 4 بیت کنترل برای کانال A:
// D15 (A/B): 0 (کانال A)
// D14 (BUF): 1 (بافر شده - توصیه شده)
// D13 (GA): 0 (گین 2x)
// D12 (SHDN): 1 (فعال)
// بیتهای کنترل
command = (channel == 0) ? 0x3000 : 0xB000; // 0x3000 برای کانال A با Gain=2x
// اضافه کردن 12 بیت داده
// به دلیل ساختار سختافزاری MCP4922، 12 بیت داده در بیتهای D0-D11 قرار میگیرند.
// اما در ارسال SPI، دادههای 12 بیتی باید در 16 بیت فرستاده شوند.
// در این حالت، 12 بیت داده باید 4 بیت به چپ شیفت داده شوند تا جای Control Word را پر کنند.
// تصحیح: MCP4922 از 16 بیت به صورت پیوسته استفاده میکند که 4 بیت اول آن کنترل است.
// Control Bits: A/B, BUF, GA, SHDN
// Data Bits: D11, D10, ..., D0
// 0x3000 = 0011 0000 0000 0000 (Control Word A, BUF=1, GA=2x, SHDN=1)
// اگر بخواهیم D11...D0 را اضافه کنیم، باید آن را به سمت راست Control Word اضافه کنیم.
command |= (value & 0x0FFF);
// اکنون باید بایتها را برای انتقال SPI درست کنیم (16 بیت را به 2 بایت تبدیل کنیم)
// برای سادگی، ما در تابع ارسال، 2 بایت را مستقیماً از یک uint16_t میسازیم.
return command;
}
// تابع ارسال داده 16 بیتی به DAC
int send_dac_data(int fd, uint16_t command) {
int ret;
// تبدیل 16 بیت به 2 بایت (Big Endian)
uint8_t tx[2];
tx[0] = (command >> 8) & 0xFF; // بایت بالا (شامل Control Bits)
tx[1] = command & 0xFF; // بایت پایین (شامل Data Bits)
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = 0, // ما از DAC چیزی نمیخوانیم
.len = 2, // ارسال 2 بایت
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
// اجرای انتقال SPI
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
perror("can't send spi message");
return -1;
}
return 0;
}
int main(int argc, char *argv[]) {
int fd;
// باز کردن دستگاه SPI
fd = open(device, O_RDWR);
if (fd < 0) {
perror("can't open device");
return 1;
}
// تنظیم مد
if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) {
perror("can't set spi mode");
return 1;
}
// تنظیم طول کلمه
if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1) {
perror("can't set bits per word");
return 1;
}
// تنظیم حداکثر سرعت
if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1) {
perror("can't set max speed hz");
return 1;
}
printf("SPI MCP4922 Interface Started at %d Hz\n", speed);
// حلقه اصلی: تولید موج دندانارهای
uint16_t dacValue = 0;
while (1) {
// ساخت دستور (کانال A، Gain=2x، فعال)
uint16_t command = build_command(0, dacValue); // 0 برای کانال A
// ارسال داده
if (send_dac_data(fd, command) == -1) {
break; // در صورت خطا، خارج شو
}
// افزایش مقدار
dacValue++;
if (dacValue > 4095) { // 12 بیت = 0 تا 4095
dacValue = 0;
}
// نکته: هیچ تاخیری (delay) در این حلقه قرار داده نشده است تا سرعت حداکثر باشد.
// فرکانس بهروزرسانی در خروجی VOUT A برابر است با (20,000,000 / 16) Hz = 1.25 MHz
}
close(fd);
return 0;
}
۳. کامپایل و اجرا
- کامپایل کردن:
gcc -o dac_spi dac_spi.c - اجرا کردن:(نیاز به دسترسی به دستگاه /dev/spidev0.0 دارد، پس با sudo اجرا کنید.)Bash
sudo ./dac_spi
نتیجه
با اجرای این برنامه، سیگنال ولتاژ خروجی (VOUT A) به سرعت از ۰ ولت شروع کرده و به حداکثر ولتاژ (۲ برابر ولتاژ مرجع، $2 \times V_{REF}$) میرسد و سپس بلافاصله به صفر باز میگردد (موج دندانارهای).
- حداکثر سرعت نمونهبرداری (Sampling Rate): با تنظیم کلاک روی 20 مگاهرتز، هر نمونه ۱۶ بیت زمان میبرد. بنابراین، حداکثر نرخ نمونهبرداری حدود $1.25$ میلیون نمونه بر ثانیه (Mega Samples Per Second – MSPS) خواهد بود.
اگر در محیط واقعی به دلیل نویز یا سیمکشی نامناسب در این سرعت دچار مشکل شدید، میتوانید مقدار speed را به 10,000,000 (10 MHz) کاهش دهید.
سایت آموزشی الکترونیک و کامپیوتر اوپن مقاله های آموزشی الکترونیک و کامپیوتر و فن آوری