# C Data Structures: enum, struct, union, bitfield, typedef This docuement provides an overview of key data structures in C, including `enum`, `struct`, `union`, `bitfield`, and `typedef`. These structures are essential for organizing data in embedded systems programming, offering flexibility in memory management and data representation. # Enum in C for Microcontroller Programming In C, an `enum` (short for "enumeration") is a user-defined data type that consists of integral constants. It is often used in microcontroller programming to represent a set of related values with meaningful names instead of using arbitrary numbers. This makes the code more readable and easier to manage. Enums are particularly useful when dealing with different states, modes, or flags within embedded systems like microcontrollers. They allow you to assign meaningful names to the different possible states of a system, improving code clarity and maintenance. ## Syntax The syntax for declaring an enum is as follows: ```c enum enum_name { value1 = x, value2 = y, value3 = z, ... }; ``` Where: - `enum_name` is the name of the enumeration. - `value1`, `value2`, `value3`, etc., are the identifiers for the enum values. - `x`, `y`, `z`, etc., are optional integer values. If not provided, the values default to `0`, and each subsequent value increments by `1`. ## Example: Enum for LED States Here’s an example of using enums to represent the different states of an LED on a microcontroller: ```c #include /* Defining an enumeration for LED states */ enum LEDState { OFF = 0, /**< LED is off */ ON = 1, /**< LED is on */ BLINK = 2 /**< LED is blinking */ }; int main() { enum LEDState currentState; /**< Declare a variable of type enum LEDState */ /* Set the LED state to ON */ currentState = ON; /**< LED is turned ON */ if (currentState == ON) { printf("LED is ON\n"); } /* Change the LED state to BLINK */ currentState = BLINK; /**< LED is blinking */ if (currentState == BLINK) { printf("LED is BLINKING\n"); } return 0; } ``` ### Explanation: - We define an enum `LEDState` with three possible values: `OFF`, `ON`, and `BLINK`. - Each of these values corresponds to an integer: `0`, `1`, and `2`, respectively. - In the `main` function, we declare a variable `currentState` of type `enum LEDState` and change its value. - The program then prints the status of the LED based on the value of `currentState`. ## Benefits of Using Enums in Microcontroller Programming 1. **Code Readability**: Enums allow you to use descriptive names for different states or modes (e.g., `LED_ON` instead of just `1`). 2. **Type Safety**: Enums provide a level of type safety by ensuring that only valid values from the set can be assigned to variables of the enum type. 3. **Easy Maintenance**: When you need to change a value, you can do it in one place (inside the enum definition) instead of changing numbers throughout your code. ## Using Enums to Represent Days of the Week in DS1307 RTC Module ```c #include /* Defining an enumeration for the days of the week */ enum WeekDay { SUNDAY = 0, /**< Sunday */ MONDAY = 1, /**< Monday */ TUESDAY = 2, /**< Tuesday */ WEDNESDAY = 3, /**< Wednesday */ THURSDAY = 4, /**< Thursday */ FRIDAY = 5, /**< Friday */ SATURDAY = 6 /**< Saturday */ }; void getDayFromDS1307(int day) { /* Example function to print the day based on the DS1307 value */ switch(day) { case SUNDAY: printf("The day is Sunday\n"); break; case MONDAY: printf("The day is Monday\n"); break; case TUESDAY: printf("The day is Tuesday\n"); break; case WEDNESDAY: printf("The day is Wednesday\n"); break; case THURSDAY: printf("The day is Thursday\n"); break; case FRIDAY: printf("The day is Friday\n"); break; case SATURDAY: printf("The day is Saturday\n"); break; default: printf("Invalid day value\n"); } } int main() { /* Simulating reading day value (let's assume the DS1307 returns 3 for Wednesday) */ int dayFromDS1307 = 3; /**< Assume we read Wednesday from DS1307 */ /* Get the day of the week from DS1307 */ getDayFromDS1307(dayFromDS1307); /**< Outputs: The day is Wednesday */ return 0; } ``` ### Explanation: - The enum `WeekDay` defines the seven days of the week, where each day is assigned a unique integer value starting from `0` (for Sunday) up to `6` (for Saturday). - The `getDayFromDS1307` function takes a `day` value (which simulates the value read from a DS1307 RTC module) and prints the corresponding day of the week. - In the `main` function, we simulate reading a value (in this case, `3` for Wednesday) from the DS1307 and pass it to `getDayFromDS1307` to print the corresponding day. ## Best Practices 1. **Use Enums for States and Modes**: Whenever you need to represent different states or modes (such as device states, pin modes, or communication protocols), consider using enums for clarity. 2. **Avoid Magic Numbers**: Never use arbitrary numbers (magic numbers) for setting states or modes. Instead, use meaningful enum names to make the code easier to understand. 3. **Initialization**: Always initialize enum variables explicitly to avoid using undefined values. 4. **Avoid Redundant Values**: Ensure that enum values are distinct and logically organized. Redundant or overlapping values can create confusion and bugs. ## Conclusion Enums in C are a powerful tool for making embedded systems programming more readable and maintainable. By using enums, you can assign meaningful names to different states or configurations, improving both code clarity and type safety. Whether you're working with device states, GPIO modes, or communication protocols, enums are an essential tool for any microcontroller project. --- # Struct in C for Microcontroller Programming In C, a `struct` (short for "structure") is a user-defined data type that allows you to group different data types under a single name. Each member of a struct can be of any data type, and the struct helps organize and manage related data more effectively. In microcontroller programming, structs are commonly used to group related hardware configuration settings, sensor data, or control parameters. Using structs in embedded systems can greatly enhance code organization, readability, and maintainability, especially when dealing with complex systems where multiple variables need to be passed together or stored in a single unit. ## Syntax The syntax for declaring a struct is as follows: ```c struct struct_name { type member1; type member2; type member3; ... }; ``` Where: - `struct_name` is the name of the struct. - `member1`, `member2`, `member3`, etc., are the names of the struct members. - `type` is the data type of each member (e.g., `int`, `float`, `char`, or even other structs). ```c #include /* Defining a struct to represent a sensor reading */ struct SensorData { float temperature; /**< Temperature reading in Celsius */ float humidity; /**< Humidity reading in percentage */ int sensorID; /**< Sensor ID */ }; /* Function to display sensor data */ void displaySensorData(struct SensorData sensor) { printf("Sensor ID: %d\n", sensor.sensorID); printf("Temperature: %.2f°C\n", sensor.temperature); printf("Humidity: %.2f%%\n", sensor.humidity); } int main() { /* Declare and initialize a sensor data struct */ struct SensorData sensor1 = {23.5, 60.0, 101}; /**< Sensor ID 101, temperature 23.5°C, humidity 60% */ /* Display sensor data */ displaySensorData(sensor1); return 0; } ``` ### Explanation: - The `struct SensorData` defines a structure to represent sensor readings with three members: - `temperature`: A float to store the temperature value in Celsius. - `humidity`: A float to store the humidity value in percentage. - `sensorID`: An integer to uniquely identify the sensor. - In the `main` function, we declare and initialize a `SensorData` struct with sample values representing a sensor (ID 101, temperature 23.5°C, humidity 60%). - The `displaySensorData` function takes a `SensorData` struct as an argument and prints the sensor’s ID, temperature, and humidity to the console. This example demonstrates how structs can be used to organize related data, such as sensor readings, and make code more manageable in embedded systems. ## Benefits of Using Structs in Microcontroller Programming 1. **Data Organization**: Structs allow you to group related data together, which makes the code more organized and easier to understand. 2. **Improved Readability**: Using structs makes your code more readable because related information is stored in one logical unit rather than scattered across individual variables. 3. **Efficient Data Handling**: Structs allow you to pass multiple related variables as a single unit (i.e., the struct) to functions or between different parts of the program, reducing the number of arguments you need to manage. 4. **Memory Efficiency**: In embedded systems, structs can be used to manage memory more efficiently by organizing different types of data together, making it easier to access and manipulate them. ## Best Practices 1. **Use Structs for Related Data**: Structs are ideal when you need to group multiple related variables together. Use them to represent sensor readings, hardware configurations, control parameters, etc. 2. **Avoid Large Structs**: While structs can contain many members, avoid making them too large in memory-constrained environments. Large structs can waste memory and reduce performance. 3. **Use typedef for Simplicity**: You can use `typedef` to simplify the syntax when working with structs, especially in embedded systems where you might need to define many structs. ```c typedef struct { float temperature; /**< Temperature reading in Celsius */ float humidity; /**< Humidity reading in percentage */ int sensorID; /**< Sensor ID */ } SensorData; ``` This allows you to define a struct without needing to repeatedly write `struct` before its name. 4. **Initialize Structs Properly**: When declaring and initializing structs, ensure that all members are initialized. This prevents issues with uninitialized variables and undefined behavior. ## Conclusion Structs in C are an essential tool for managing and organizing complex data in microcontroller programming. They provide a way to group related variables together into a single, manageable unit, making the code more readable and maintainable. Whether you're working with hardware configurations, sensor data, or other complex structures, using structs allows you to handle data efficiently and in a more structured way. --- # Union in C for Microcontroller Programming In C, a `union` is a special data type that allows you to store different data types in the same memory location. Unlike a `struct`, where each member has its own memory space, a union uses a single shared memory space for all its members. This means that all members of a union will overlap in memory, and only one member can hold a value at a time. Unions are particularly useful in embedded systems and microcontroller programming when you need to efficiently manage memory usage, especially when different types of data need to be stored at different times, but not simultaneously. ## Syntax The syntax for declaring a union is as follows: ```c union union_name { type member1; type member2; type member3; ... }; ``` Where: - `union_name` is the name of the union. - `member1`, `member2`, `member3`, etc., are the names of the union members. - `type` is the data type of each member (e.g., `int`, `float`, `char`, etc.). ### Example: Union for Different Data Representations Here’s an example of using a union to represent different ways to store the same data (an integer) in a microcontroller: ```c #include /* Defining a union to store different representations of an integer */ union Data { int i; /**< Integer representation */ float f; /**< Float representation */ char str[20]; /**< String representation */ }; int main() { union Data data; /**< Declare a variable of type union Data */ /* Assign an integer value to the union */ data.i = 10; /**< Store an integer in the union */ printf("Integer: %d\n", data.i); /* Assign a float value to the union (overwrites the previous data) */ data.f = 3.14; /**< Store a float in the union */ printf("Float: %.2f\n", data.f); /* Assign a string value to the union (overwrites the previous data) */ snprintf(data.str, sizeof(data.str), "Hello, World!"); /**< Store a string in the union */ printf("String: %s\n", data.str); return 0; } ``` ### Explanation: - The `union Data` defines a union with three members: an integer `i`, a float `f`, and a string `str` (a character array). - In the `main` function, we assign different types of data to the union members. Since the union shares the same memory for all members, each assignment overwrites the previous one. - The output will show the most recent assignment to the union, and you can observe how the union can hold different types of data at different times. ## Benefits of Using Unions in Microcontroller Programming 1. **Memory Efficiency**: Unions are memory efficient because they share the same memory space for all their members. This is especially beneficial in memory-constrained environments like microcontrollers. 2. **Flexible Data Storage**: Unions allow you to store different data types in the same memory location, making it easy to manage different data formats (e.g., integer, float, string) for the same logical entity. 3. **Efficient Handling of Multiple Data Representations**: Unions are useful when a variable can be represented in multiple formats, such as when you need to interpret data in both integer and floating-point formats or when working with packed data. ## Converting a 32-bit Integer to Four 8-bit Bytes Using a Union ```c #include #include /* Defining a union to convert a 32-bit unsigned integer to four 8-bit unsigned integers */ union Converter { uint32_t fullValue; /**< 32-bit unsigned integer */ uint8_t byte[4]; /**< Array of 4 bytes (uint8_t) */ }; int main() { union Converter converter; /**< Declare a variable of type union Converter */ /* Assign a 32-bit value to the union */ converter.fullValue = 0x12345678; /**< Example 32-bit value */ printf("32-bit value: 0x%X\n", converter.fullValue); /**< Print the full 32-bit value */ /* Print the 4 bytes */ printf("Byte 1: 0x%X\n", converter.byte[0]); /**< Output: 0x78 */ printf("Byte 2: 0x%X\n", converter.byte[1]); /**< Output: 0x56 */ printf("Byte 3: 0x%X\n", converter.byte[2]); /**< Output: 0x34 */ printf("Byte 4: 0x%X\n", converter.byte[3]); /**< Output: 0x12 */ return 0; } ``` ### Explanation: - The `union Converter` represents a 32-bit unsigned integer (`fullValue`) and an array of four 8-bit unsigned integers (`byte[4]`), all sharing the same memory location. - When we assign a value to `fullValue`, the union allows us to access the same data as individual bytes in the `byte` array. - In the `main` function, we assign a 32-bit value (`0x12345678`) to `fullValue` and then print both the full 32-bit value and the four individual bytes (`byte[0]`, `byte[1]`, `byte[2]`, `byte[3]`), demonstrating how a 32-bit number can be split into four 8-bit parts. ## Best Practices 1. **Use Unions for Memory-Constrained Environments**: In embedded systems with limited memory, unions are a good choice for saving memory space when you only need to store one type of data at a time. 2. **Access the Correct Member**: Be careful to only access the union member that was most recently assigned. Accessing other members will result in incorrect data since all members share the same memory location. 3. **Avoid Complex Unions with Many Members**: Unions are simple, but they can become tricky to manage if you have too many members, especially if the different types have significantly different memory sizes. 4. **Use Bitfields Wisely**: When using bitfields inside unions, ensure the system's endianness and alignment requirements are considered, as bitfields may be sensitive to these factors. ## Conclusion Unions in C are a powerful tool for efficient memory management, especially in microcontroller programming where memory resources are often limited. They allow you to store multiple types of data in the same memory location, making them ideal for applications where data representation may change over time. Whether you need to handle register configurations, different data types, or compact data structures, unions provide a flexible and memory-efficient solution. --- # Bitfield in C for Microcontroller Programming In C, a **bitfield** is a feature that allows you to specify the number of bits allocated for a particular member within a `struct`. Bitfields are useful when you need to work with data at the bit level, such as manipulating flags, control registers, or other compact data formats in microcontroller programming. By defining bitfields, you can optimize memory usage and precisely control how much space each field occupies in a struct. Bitfields are particularly important in embedded systems, where the efficient use of memory is crucial, and hardware registers are often accessed in bitwise operations. ## Syntax A bitfield is declared within a `struct` and uses a colon (`:`) followed by the number of bits allocated for that field. Here’s the general syntax: ```c struct struct_name { type member_name : number_of_bits; }; ``` Where: - `struct_name` is the name of the struct. - `type` is the data type of the field (usually `int` or `unsigned` types). - `member_name` is the name of the bitfield. - `number_of_bits` specifies how many bits this field will occupy. ## Example: Using Bitfields and Byte Access for GPIO Register Configuration ```c #include #include // Define a bitfield for GPIO pin configuration typedef struct // 1 Byte { uint8_t PIN0 : 1; uint8_t PIN1 : 1; uint8_t PIN2 : 1; uint8_t PIN3 : 1; uint8_t PIN4 : 1; uint8_t PIN5 : 1; uint8_t PIN6 : 1; uint8_t PIN7 : 1; } GPIO_pinType; // Define a union to access the GPIO registers either as a byte or as individual bits typedef union { uint8_t Reg; // Full byte access GPIO_pinType Bit; // Bit-level access } U_type; // Define a structure to represent the GPIO registers (PORT, DDR, PIN) typedef struct { U_type PIN; // 0x23 U_type DDR; // 0x24 U_type PORT; // 0x25 } GPIO_Type; // Define GPIO base addresses for PORTB and GPIOD #define GPIOB ((GPIO_Type*) 0x23) #define GPIOD ((GPIO_Type*) 0x29) int main(void) { // Set DDR for PORTB to 0xAA (pins 1, 3, 5, 7 as output, others as input) GPIOB->DDR.Reg = 0xAA; // Set DDR for GPIOD to make PIN0 as output (using bit-level access) GPIOD->DDR.Bit.PIN0 = 1; while(1) { // Toggle PORTB with 0xAA (pins 1, 3, 5, 7 HIGH, others LOW) GPIOB->PORT.Reg = 0xAA; _delay_ms(1000); // Delay for 1 second // Set PORTB to 0x00 (all pins LOW) GPIOB->PORT.Reg = 0x00; _delay_ms(1000); // Delay for 1 second } } ``` ### Explanation: 1. **Bitfield Access**: - The `GPIO_pinType` struct uses bitfields to define individual bits for each pin (PIN0 to PIN7). This allows you to access each pin individually as a single bit (bit-level access). 2. **Union for Both Byte and Bit-Level Access**: - The `U_type` union allows access to the GPIO registers either as a full byte (`Reg`) or at the individual bit level (`Bit`). - `Reg` provides direct access to the entire register as a byte, while `Bit` allows access to the individual pins as bitfields. 3. **GPIO_Type Structure**: - The `GPIO_Type` struct represents the GPIO registers, consisting of `PIN`, `DDR`, and `PORT` registers. Each of these registers is accessed via the `U_type` union, enabling both byte-level and bit-level access to each register. 4. **GPIO Base Addresses**: - The base addresses for `PORTB` and `GPIOD` are defined using `#define`, allowing direct access to these ports using the `GPIOB` and `GPIOD` pointers in the code. 5. **Manipulating Registers**: - In the `main` function, we first set the `DDR` (data direction register) of `PORTB` using byte-level access (`GPIOB->DDR.Reg = 0xAA`). - We then set `PIN0` of `GPIOD` as an output using bit-level access (`GPIOD->DDR.Bit.PIN0 = 1`). - Finally, we toggle `PORTB` between `0xAA` (some pins HIGH, others LOW) and `0x00` (all pins LOW) in a loop, with a 1-second delay. This example demonstrates how to use both byte-level and bit-level access for controlling GPIO pins and registers on a microcontroller. ## Benefits of Using Bitfields in Microcontroller Programming 1. **Memory Optimization**: Bitfields allow you to pack multiple values into a small amount of memory. This is particularly beneficial in memory-constrained environments like microcontrollers, where each byte counts. 2. **Access to Specific Bits**: Bitfields enable direct access to individual bits or groups of bits within a larger data structure, making it easier to manipulate flags or control registers. 3. **Readable Code**: By naming each bitfield appropriately, you can make your code more readable and self-documenting, especially when working with hardware registers or control flags. 4. **Efficient Register Management**: Bitfields are often used to define control registers in embedded systems, allowing you to access and modify specific bits without affecting others. ## Best Practices 1. **Use Bitfields for Flags and Small Values**: Bitfields are ideal for storing flags, control bits, and other small values that need to be accessed at the bit level. For example, flags in a hardware control register or status bits can be effectively managed with bitfields. 2. **Align Bitfields Properly**: Be mindful of the alignment and padding rules in your target architecture. Some compilers may introduce padding between bitfields to align them to specific boundaries. Check the memory layout to ensure your bitfields are packed as expected. 3. **Limit the Number of Bits in a Bitfield**: While bitfields save memory, having too many bitfields in a single struct can cause inefficient memory usage or issues with alignment. Keep bitfields to a reasonable size to ensure efficient memory usage. 4. **Use `unsigned` Types**: It’s generally a good practice to use `unsigned` types for bitfields, especially when you are working with flags, to avoid sign extension issues and ensure that only the required bits are used. ## Conclusion Bitfields in C are a powerful tool for embedded systems programming, offering precise control over memory usage and enabling efficient manipulation of individual bits in a data structure. They are widely used for representing flags, control registers, and compact data formats in microcontroller applications. By using bitfields, you can optimize your code for memory-constrained environments while keeping your code clear and maintainable. --- # typedef in C for Microcontroller Programming In C, `typedef` is a keyword that allows you to create aliases for existing data types. This can make code more readable, simplify complex data type declarations, and provide more flexibility in how types are defined. In embedded systems programming, `typedef` is commonly used to define custom types for hardware configurations, data structures, and other types that will be used frequently in the program. By using `typedef`, you can simplify your code and make it more portable, since you can easily change the underlying type definition in one place without affecting the entire program. ## Syntax The syntax for `typedef` is as follows: ```c typedef existing_type new_type_name; ``` Where: - `existing_type` is the type you want to alias (e.g., `int`, `unsigned char`, `struct`, etc.). - `new_type_name` is the new name that you want to use for the existing type. ### Example: Using typedef to Simplify Data Types Here’s a simple example where `typedef` is used to create an alias for a standard data type. ```c #include typedef unsigned char byte; /**< Alias for unsigned char */ int main() { byte data = 255; /**< Using the alias 'byte' */ printf("Data: %u\n", data); /**< Output: 255 */ return 0; } ``` ### Explanation: - The `typedef unsigned char byte;` creates an alias named `byte` for `unsigned char`. - In the `main` function, we use `byte` as a shorthand for `unsigned char`, making the code cleaner and more readable. ## Example: typedef for Structs in Microcontroller Programming ```c #include /* Defining a struct to represent an ADC configuration */ typedef struct { int channel; /**< ADC channel number (e.g., 0, 1, 2, etc.) */ int reference; /**< ADC reference voltage (e.g., VCC, Internal Reference) */ int prescaler; /**< ADC prescaler for sampling rate */ int result; /**< ADC result (digital output of the conversion) */ } ADCConfig; int main() { /* Declare and initialize an ADC configuration */ ADCConfig adc1 = {0, 5, 64, 0}; /**< Channel 0, VCC reference, prescaler 64, result initialized to 0 */ /* Simulate an ADC conversion */ adc1.result = 1023; /**< Assume maximum ADC value after conversion (10-bit resolution) */ /* Print the ADC configuration and result */ printf("ADC Channel: %d\n", adc1.channel); /**< Output: 0 */ printf("ADC Reference: %dV\n", adc1.reference); /**< Output: 5V */ printf("ADC Prescaler: %d\n", adc1.prescaler); /**< Output: 64 */ printf("ADC Result: %d\n", adc1.result); /**< Output: 1023 */ return 0; } ``` ### Explanation: - The `typedef struct { ... } ADCConfig;` creates an alias named `ADCConfig` for the struct that holds the ADC configuration, making it easier to declare and manage ADC settings. - `channel`: The ADC channel number (e.g., 0, 1, 2) that you want to read from. - `reference`: The reference voltage for the ADC conversion (e.g., VCC or internal reference). - `prescaler`: The prescaler value used to adjust the ADC's sample rate. - `result`: The digital result of the ADC conversion. - In the `main` function, we initialize an ADC configuration for channel 0 with a VCC reference and a prescaler value of 64. The ADC result is then simulated as 1023 (the maximum 10-bit ADC result). - The configuration and the simulated result are printed to the console. This example demonstrates how `typedef` simplifies the usage of complex data structures like ADC settings, which is a common scenario in embedded systems. ## Benefits of Using typedef in Microcontroller Programming 1. **Code Readability**: `typedef` simplifies complex data type definitions and makes the code more readable. By using aliases, you can avoid long, cumbersome type declarations, especially when dealing with structs or function pointers. 2. **Portability**: Using `typedef` makes it easier to change underlying types without affecting the rest of the program. For example, if you need to switch from a 16-bit to a 32-bit integer type, you can simply modify the `typedef` definition and the rest of the code will remain intact. 3. **Easier Struct and Function Pointer Usage**: `typedef` simplifies the use of structs and function pointers, which are commonly used in embedded systems for managing hardware configurations, sensor data, and callback functions. 4. **Simplify Hardware Register Access**: When working with hardware registers and bitfields, `typedef` can be used to create meaningful names for registers or fields, making the code clearer and more maintainable. ## Best Practices 1. **Use Descriptive Names**: When using `typedef`, choose meaningful names that describe the data type or the purpose of the type. For example, use `GPIOConfig` for GPIO configurations instead of generic names like `Config`. 2. **Avoid Overuse**: While `typedef` can simplify code, overusing it can reduce code clarity. It's best used when it genuinely improves readability and understanding. 3. **Keep Function Pointer Typedefs Clean**: For function pointers, use `typedef` to define the function signature, but avoid overcomplicating it with too many levels of indirection. 4. **Maintain Consistency**: When defining types for hardware registers or structures, ensure the `typedef` names are consistent and meaningful across your project. This helps to maintain code clarity and reduces confusion. ## Conclusion The `typedef` keyword in C is a powerful tool for creating type aliases that can simplify code, enhance readability, and improve portability. In microcontroller programming, it is particularly useful for defining custom types for hardware configurations, structures, and function pointers. By using `typedef`, you can write cleaner, more maintainable code and reduce complexity when managing different data types and structures in your embedded systems. # 🌟 Support Me If you found this repository useful: - Subscribe to my [YouTube Channel](https://www.youtube.com/@aKaReZa75). - Share this repository with others. - Give this repository and my other repositories a star. - Follow my [GitHub account](https://github.com/aKaReZa75). # ✉️ Contact Me Feel free to reach out to me through any of the following platforms: - 📧 [Email: aKaReZa75@gmail.com](mailto:aKaReZa75@gmail.com) - 🎥 [YouTube: @aKaReZa75](https://www.youtube.com/@aKaReZa75) - 💼 [LinkedIn: @akareza75](https://www.linkedin.com/in/akareza75)