#include <avr/io.h>
#include <util/delay.h>
// --- پیکربندی پایهها ---
// پایههای سگمنت (مثلاً PORTD: D0-D6)
#define SEGMENT_PORT PORTD
#define SEGMENT_DDR DDRD
#define SEGMENT_MASK 0xFF // (1<<PD0) | (1<<PD1) | ... | (1<<PD6)
// پایههای انتخاب رقم (مثلاً PORTB: B0-B3)
#define DIGIT_PORT PORTB
#define DIGIT_DDR DDRB
// پایههای فعالکننده رقم (Common Cathode)
#define DIGIT_1_PIN (1<<PB0)
#define DIGIT_2_PIN (1<<PB1)
#define DIGIT_3_PIN (1<<PB2)
#define DIGIT_4_PIN (1<<PB3)
// --- نگاشت هگز به سگمنت (Common Cathode) ---
// الگوهای روشن برای 0-9 و A-F
// DP (نقطه اعشار) در اینجا لحاظ نشده برای سادگی
const uint8_t hex_to_segments[] = {
// 0 1 2 3 4 5 6 7
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
// 8 9 A B C D E F
0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71
};
// --- متغیرهای سراسری ---
uint8_t display_buffer[4] = {0}; // بافر برای نگهداری 4 رقم هگز برای نمایش
volatile uint8_t current_digit = 0; // رقمی که الان باید نمایش داده شود (برای مالتیپلکسینگ)
// --- توابع کمکی ---
// فعال کردن یک رقم خاص و نمایش الگو
void set_digit_and_segments(uint8_t digit_index, uint8_t hex_value) {
// خاموش کردن همه ارقام قبلی
DIGIT_PORT |= (DIGIT_1_PIN | DIGIT_2_PIN | DIGIT_3_PIN | DIGIT_4_PIN); // همه را High می کنیم تا خاموش شوند (برای Common Cathode)
// استخراج الگوی سگمنت برای مقدار هگز
uint8_t segments = hex_to_segments[hex_value & 0x0F]; // فقط 4 بیت پایین استفاده می شود
// پاک کردن بیتهای سگمنت قبلی
SEGMENT_PORT &= ~SEGMENT_MASK;
// تنظیم بیتهای سگمنت برای کاراکتر فعلی
SEGMENT_PORT |= segments;
// فعال کردن رقم مورد نظر
switch (digit_index) {
case 0: DIGIT_PORT &= ~DIGIT_1_PIN; break; // رقم اول (چپ ترین)
case 1: DIGIT_PORT &= ~DIGIT_2_PIN; break; // رقم دوم
case 2: DIGIT_PORT &= ~DIGIT_3_PIN; break; // رقم سوم
case 3: DIGIT_PORT &= ~DIGIT_4_PIN; break; // رقم چهارم (راست ترین)
}
}
// تابع اصلی آپدیت صفحه 7-segment (این تابع باید خیلی سریع تکرار شود)
void update_display() {
// خاموش کردن رقم قبلی
DIGIT_PORT |= (DIGIT_1_PIN | DIGIT_2_PIN | DIGIT_3_PIN | DIGIT_4_PIN);
// نمایش رقم فعلی با مقدار از بافر
set_digit_and_segments(current_digit, display_buffer[current_digit]);
// رفتن به رقم بعدی برای نمایش بعدی
current_digit++;
if (current_digit >= 4) {
current_digit = 0;
}
}
// تابعی برای تنظیم آدرس مورد نظر روی صفحه
void display_i2c_address(uint8_t address) {
// تبدیل آدرس 8 بیتی به 4 رقم هگز
display_buffer[0] = (address >> 4) & 0x0F; // رقم اول (بالاترین)
display_buffer[1] = address & 0x0F; // رقم دوم
// برای سادگی، دو رقم آخر را در 4 رقم نمایش میدهیم
// مثلاً 0x3C -> " 3C" یا "003C"
// اگر میخواهید "003C" نمایش داده شود:
display_buffer[0] = (address >> 4) & 0x0F; // رقم اول (مثلاً 3 از 0x3C)
display_buffer[1] = address & 0x0F; // رقم دوم (مثلاً C از 0x3C)
display_buffer[2] = 0x00; // صفر (برای نمایش 003C)
display_buffer[3] = 0x00; // صفر
}
// --- تابع اصلی (main) ---
int main(void) {
// --- تنظیمات اولیه ---
// فعال کردن DDR برای پایههای سگمنت و ارقام
SEGMENT_DDR |= SEGMENT_MASK;
DIGIT_DDR |= (DIGIT_1_PIN | DIGIT_2_PIN | DIGIT_3_PIN | DIGIT_4_PIN);
// اطمینان از خاموش بودن همه ارقام در ابتدا
DIGIT_PORT |= (DIGIT_1_PIN | DIGIT_2_PIN | DIGIT_3_PIN | DIGIT_4_PIN);
// --- اینجا باید توابع راهاندازی I2C میکروکنترلر خود را فراخوانی کنید ---
// i2c_init(); // تابع فرضی برای شروع I2C
uint8_t found_address = 0; // آخرین آدرس پیدا شده
// --- حلقه اصلی اسکن ---
for (uint8_t address = 0x08; address < 0x78; address++) {
// --- تلاش برای برقراری ارتباط با دستگاه ---
// این قسمت کد I2C شما است.
// فرض کنید تابعی دارید به نام `i2c_probe(address)` که 1 برمیگرداند اگر ACK بگیرد، 0 در غیر اینصورت.
// bus_start();
// uint8_t error = bus_send_byte((address << 1) | 0); // ارسال آدرس برای نوشتن
// bus_stop();
//
// if (error == 0) { // یعنی ACK گرفته شده
// found_address = address; // آدرس را ذخیره کن
// }
// برای تست، فرض میکنیم آدرس 0x3C پیدا شده
if (address == 0x3C) { // آدرس فرضی که پیدا شده
found_address = address;
// نمایش آدرس پیدا شده روی 7-segment
display_i2c_address(found_address);
// مکث 1.5 ثانیهای
_delay_ms(1500);
}
// --- آپدیت صفحه نمایش در هر تکرار حلقه (برای مالتیپلکسینگ) ---
// این بخش باید خیلی سریع اتفاق بیفتد.
// اگر از تایمر و ISR برای مالتیپلکسینگ استفاده کنید، بهتر است.
// اگر نه، اینجا هم میشود فراخوانی کرد ولی با وقفه.
update_display();
_delay_ms(2); // تاخیر کوتاه بین تغییر ارقام (فرکانس آپدیت حدود 250Hz)
}
// اگر هیچ دستگاهی پیدا نشد، نمایش "----" یا "None"
if (found_address == 0) {
// display_buffer = {'-', '-', '-', '-'}; // نیاز به تابع نمایش کاراکتر خاص
// نمایش "----" یا "NONE" رو بسته به فضای نمایش شما
}
// --- حلقه بینهایت ---
while (1) {
// در حالت عادی، صفحه نمایش باید توسط یک تایمر و ISR آپدیت شود
// اما اگر این را در حلقه main گذاشتیم، حداقل باید آپدیت صفحه انجام شود
update_display();
_delay_ms(2); // تنظیم این تاخیر، سرعت مالتیپلکسینگ را تعیین میکند
}
return 0;
}