• Microcontroller Architecture

    These are the different classification of microcontroller architecture.

    ISA

    ISA stands for Instruction Set Architecture, which is a set of instructions and commands that a CPU can execute. It defines the way in which a program communicates with the processor, including the format of instructions, the way they are decoded and executed, and how data is moved between the CPU and memory.

    There are two main types of ISA: Reduced Instruction Set Computer (RISC) and Complex Instruction Set Computer (CISC). RISC architectures are designed with a simplified set of instructions, where each instruction performs a very specific operation. CISC architectures, on the other hand, have a larger set of instructions, some of which can perform multiple operations.

    Some popular examples of RISC architectures include ARM and MIPS, while x86 is an example of a CISC architecture. RISC architectures tend to be more efficient and have better performance in certain tasks, such as those involving a lot of arithmetic or logical operations. However, CISC architectures are better suited for tasks that involve more complex operations, such as those required for multimedia or gaming.

    ISA also includes the concept of register transfer, which involves moving data between registers and memory. A register is a small amount of memory located inside the CPU, which can be accessed much faster than main memory. By using registers, programs can perform operations much faster and more efficiently than if they had to continually access main memory.

    Overall, the ISA plays a crucial role in the performance and efficiency of a CPU, as it defines how programs interact with the processor and how data is moved between different parts of the system. As such, choosing the right ISA for a particular application is an important consideration for CPU designers and software developers alike.

    RISC vs. CISC

    RISC (Reduced Instruction Set Computer) and CISC (Complex Instruction Set Computer) are two fundamental types of CPU (Central Processing Unit) architectures. The main difference between RISC and CISC architecture is in the number and complexity of instructions they support.

    RISC ArchitectureCISC Architecture
    RISC processors have a smaller set of simple and basic instructions.CISC processors have a larger and more complex set of instructions.
    RISC processors use simple addressing modes.CISC processors use complex addressing modes.
    RISC processors have a large number of general-purpose registers.CISC processors have a small number of general-purpose registers.
    RISC processors perform most arithmetic and logical operations in registers.CISC processors perform most arithmetic and logical operations in memory.
    RISC processors have a uniform instruction format.CISC processors have a non-uniform instruction format.
    RISC processors rely on software for more advanced functionality.CISC processors have hardware support for more advanced functionality.

    Overall, RISC architecture aims for simplicity and speed, while CISC architecture aims for versatility and flexibility.

    Harvard vs. Von Neumann Architecture

    CriteriaHarvard ArchitectureVon Neumann Architecture
    Data and Instruction PathSeparate data and instruction memory spacesShared memory for data and instructions
    Memory AccessSimultaneous access of data and instruction memorySequential access of data and instruction memory
    PerformanceFaster data transfer rate and higher processing speedSlower data transfer rate and lower processing speed
    ImplementationCommonly used in embedded systems and DSP applicationsUsed in most general-purpose computers and microprocessors
    Instruction Set SizeLarger instruction set to support more complex tasksSmaller instruction set

    Note: DSP stands for Digital Signal Processing.

    Pipelining

    Pipelining is a technique used in computer processor design to enhance the speed of instruction execution. It involves breaking down the execution of an instruction into multiple stages and overlapping them in such a way that multiple instructions are being executed at the same time. In pipelining, the processor is divided into several stages, and each stage performs a specific operation.

    For example, in a five-stage pipeline, the processor will be divided into five stages, such as instruction fetch, decode, execute, memory access, and write back. Each instruction will go through these stages, and while one instruction is being executed, the next instruction can be fetched, the following instruction can be decoded, and so on.

    Pipelining allows the processor to operate at a higher frequency and to process instructions more efficiently, leading to faster performance. However, pipelining also introduces some complications, such as pipeline hazards, where instructions may be dependent on each other, causing delays in the pipeline. To mitigate these issues, techniques such as forwarding and stalling can be used.

    In summary, pipelining is a technique used in computer processor design to improve performance by overlapping instruction execution. It breaks down the instruction execution into multiple stages, allowing multiple instructions to be processed simultaneously. While pipelining can improve performance, it also introduces complications that need to be addressed to ensure proper instruction execution.

    Cache memory

    Cache memory is a type of high-speed memory that is used to store frequently used data and instructions so that the processor can access them quickly. It is a small amount of memory that is built into the processor or located nearby on the motherboard.

    The main purpose of cache memory is to reduce the time it takes for the processor to access data from main memory, which is much slower than cache memory. When the processor requests data from main memory, the data is copied into the cache memory so that if the same data is requested again, it can be accessed from the cache memory instead, which is much faster.

    Cache memory is organized into multiple levels, with each level providing a different size and speed of memory. The first level of cache memory, called L1 cache, is built into the processor and is the fastest and smallest type of cache memory. The second level of cache memory, called L2 cache, is located on the motherboard and is larger and slower than L1 cache. Some processors also have a third level of cache memory, called L3 cache, which is even larger and slower than L2 cache.

    Cache memory is an important component of modern processors because it allows them to execute instructions and access data more quickly, which can greatly improve the overall performance of a system. However, the amount and speed of cache memory that a processor has can vary widely depending on the design, and can have a significant impact on its performance in different types of applications.

    Bus Architecture

    In a computer system, a bus is a communication pathway between different components of the system, such as the CPU, memory, and input/output devices. Bus architecture refers to the way in which these buses are organized and managed in a computer system.

    The three main types of bus architecture are:

    1. Single Bus Architecture: This is the simplest type of bus architecture, where all the components of the system are connected to a single bus. This bus is responsible for transmitting data between the components. However, since all the components share the same bus, there can be congestion and delays in the transmission of data.
    2. Multi-Bus Architecture: In this type of architecture, the system is divided into multiple buses that are connected together. Each bus is responsible for transmitting data between specific components of the system. This helps to reduce congestion and improve the efficiency of data transmission.
    3. Crossbar Switch Architecture: This is the most complex type of bus architecture, where a crossbar switch is used to connect all the components of the system. A crossbar switch is a network of switches that can connect any two components of the system directly. This type of architecture provides the highest level of performance, but it is also the most expensive.

    Bus architecture plays a crucial role in determining the performance and efficiency of a computer system. The choice of bus architecture depends on the requirements of the system, such as the speed and amount of data that needs to be transmitted.

  • Introduction to Microcontrollers

    If you’re studying electrical engineering or computer science, you’ve probably heard of microcontrollers. But what exactly are they, and why are they important? In this blog post, we’ll introduce you to the world of microcontrollers, and give you an overview of the different types of microcontrollers that you’ll encounter in your studies.

    What are Microcontrollers?

    A microcontroller is a small computer on a single integrated circuit. It contains a processor, memory, and input/output peripherals, all on a single chip. Microcontrollers are designed to perform specific tasks, and are commonly used in embedded systems such as consumer electronics, industrial automation, medical devices, and robotics.

    Microcontroller Architecture

    Microcontrollers have a unique architecture that sets them apart from traditional computers. They typically have limited program and data memory, and are optimized for low power consumption. They also have a variety of input/output ports and registers, as well as timers and interrupts for real-time processing. Some microcontrollers also have analog to digital converters (ADCs) and digital to analog converters (DACs) for interfacing with analog signals.

    Different Types of Microcontrollers

    There are many different types of microcontrollers available, with varying architectures, features, and capabilities. Some of the most popular microcontrollers used in universities and research labs include:

    • Arduino Uno: A simple microcontroller board based on the Atmel AVR microcontroller.
    • Raspberry Pi: A single board computer that can run a full operating system, and is often used for prototyping and development.
    • ESP8266: A low-cost Wi-Fi microcontroller designed for IoT applications.
    • ESP32: A more powerful Wi-Fi and Bluetooth-enabled microcontroller, also designed for IoT applications.
    • STM32f103ct6 blue pill: An ARM-based microcontroller commonly used in embedded systems.
    • Mini STM32 3.0: A compact version of the STM32f103rbt6.
    • PIC16f877a: A popular 8-bit microcontroller from Microchip.
    • AVR ATmega328P: Another popular 8-bit microcontroller from Atmel.
    • Raspberry Pi Pico and Raspberry Pi Pico W: A new microcontroller board from the Raspberry Pi Foundation, based on the RP2040 microcontroller.
    • ATmega 16, ATmega 32a, ATmega328p, Attiny2313: Other popular AVR microcontrollers.

    Microcontroller Development Tools

    To program and debug microcontrollers, you’ll need a set of development tools. These include an Integrated Development Environment (IDE) such as Atmel Studio or the Arduino IDE, a compiler or assembler to convert your code into machine language, and an emulator or simulator to test your code before running it on the actual microcontroller. You’ll also need debugging tools such as a logic analyzer or an oscilloscope to troubleshoot your circuits.

    Programming Languages for Microcontrollers

    Microcontrollers can be programmed in a variety of languages, including assembly language, C language, and C++ language. Assembly language is a low-level language that directly controls the microcontroller hardware, while C and C++ are higher-level languages that abstract away some of the hardware details.

    Interfacing with Peripherals

    One of the main tasks of a microcontroller is to interface with various peripherals such as LCDs, LEDs, switches, motors, sensors, and wireless modules. This requires a solid understanding of digital and analog electronics, as well as the specific protocols and communication methods used by each peripheral.

    Real-time Operating Systems (RTOS) and Multitasking

    In some applications, microcontrollers need to perform multiple tasks simultaneously, in real-time. This requires the use of a real-time operating system (RTOS) that can manage and prioritize the various tasks running on the microcontroller. An RTOS allows for efficient and reliable multitasking, ensuring that each task is executed within a specific time frame.

  • Ohm’s Law Calculator







    How to use Ohm’s law calculator

    1. Enter the known values for any two of the three variables (voltage, current, or resistance) in the input fields.
    2. Leave the input field for the unknown variable empty.
    3. Click the “Calculate” button.
    4. The calculator will calculate the value of the unknown variable and fill it into the appropriate input field.

    For example, let’s say we want to calculate the current flowing through a resistor that has a resistance of 100 ohms and a voltage of 12 volts. We would do the following:

    1. Enter 12 in the input field for voltage.
    2. Enter 100 in the input field for resistance.
    3. Leave the input field for current empty.
    4. Click the “Calculate” button.
    5. The calculator will calculate the value of the current to be 0.12 amps and fill it into the input field for current.

    Explanation of Ohm’s law

    Ohm’s Law states that the current flowing through a conductor between two points is directly proportional to the voltage across the two points, provided that the temperature and other physical conditions remain constant. This relationship is expressed mathematically as:

    V = IR

    Where:

    V is the voltage across the conductor,
    I is the current flowing through the conductor, and
    R is the resistance of the conductor.


    Code

    <script type="text/javascript">
    		function calculate() {
    			var v = document.getElementById("voltage").value;
    			var i = document.getElementById("current").value;
    			var r = document.getElementById("resistance").value;
    			if (v == "") {
    				document.getElementById("voltage").value = i * r;
    			} else if (i == "") {
    				document.getElementById("current").value = v / r;
    			} else if (r == "") {
    				document.getElementById("resistance").value = v / i;
    			}
    		}
    		function clearFields() {
    			document.getElementById("voltage").value = "";
    			document.getElementById("current").value = "";
    			document.getElementById("resistance").value = "";
    		}
    	</script>
    <style>
    		.ohm_container {
    			margin: auto;
    			width: 50%;
    			padding: 10px;
    			border: 2px solid #ccc;
    			text-align: center;
    		}
    	</style>
    <div class="ohm_container">
    <form>
    		<label for="voltage">Voltage (V):</label>
    		<input type="number" id="voltage"><br><br>
    
    		<label for="current">Current (A):</label>
    		<input type="number" id="current"><br><br>
    
    		<label for="resistance">Resistance (&#937;):</label>
    		<input type="number" id="resistance"><br><br>
    
    		<input type="button" value="Calculate" onclick="calculate()">
    
    		<input type="button" value="Clear" onclick="clearFields()">
    	</form>
    </div>
  • How to Blink LED on Mini STM32 v3.0 Discovery Board Using STM32CubeIDE

    Step 2: Configure GPIO Pins

    #define LED1_Pin GPIO_PIN_2
    #define LED1_GPIO_Port GPIOA
    #define LED2_Pin GPIO_PIN_3
    #define LED2_GPIO_Port GPIOA

    In the Project Explorer pane, expand the “Src” folder and open the “main.c” file. Scroll down to the main() function and add the following code to configure the GPIO pins:

    /* Configure GPIO pins */
    __HAL_RCC_GPIOA_CLK_ENABLE();      // Enable GPIOA clock
    GPIO_InitTypeDef GPIO_InitStruct; // GPIO configuration structure
    GPIO_InitStruct.Pin = GPIO_PIN_2; // Use pin 2 on GPIOA
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // Push-Pull output mode
    GPIO_InitStruct.Pull = GPIO_NOPULL; // No pull-up or pull-down resistors
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // Low speed
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // Initialize GPIOA with settings
    

    This code configures pin 2 on GPIOA as a push-pull output pin with no pull-up or pull-down resistors and low output speed.

    Blink the LED using HAL_GPIO_WritePin

    Add the following code inside the while(1) loop to blink the LED:

    /* Blink LED */
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); // Turn LED on
    HAL_Delay(1000); // Wait for 1 second
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // Turn LED off
    HAL_Delay(1000); // Wait for 1 second
    

    This code turns the LED on by setting the state of pin 2 on GPIOA to high, waits for 1 second, turns the LED off by setting the state of pin 2 on GPIOA to low, and then waits for 1 second again. This creates a blinking effect.

    Blink the LED using HAL_GPIO_TogglePin

    /* Blink LED */
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2); // Toggle the state of the LED
    HAL_Delay(1000); // Wait for 1 second
    

    This code toggles the state of pin 2 on GPIOA (which is connected to an LED on the Mini STM32 board) and then waits for 1 second before toggling it again. This creates a blinking effect.

    HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
    HAL_Delay(1000);
    HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);

    Code to Blink both LED’s

    HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
    HAL_Delay(1000);
    HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);

    The first line toggles the state of an LED connected to the LED1_GPIO_Port and LED1_Pin. The second line waits for 1000 milliseconds (1 second) using the HAL_Delay() function. The third line toggles the state of another LED connected to the LED2_GPIO_Port and LED2_Pin.

  • How to Generate Combinations of the Component from Four Text Files using Python & tkinter

    This program generates a random combination of components. This can be a fun program.
    It creates the combination of all the components in the four list.

    For example; if each list contains 5 words. Then the total number of combinations would be 5 x 5 x 5 x 5 = 625

    Code

    import random
    import tkinter as tk
    from tkinter import messagebox
    import itertools
    import webbrowser
    
    
    # Create the GUI window
    window = tk.Tk()
    window.title("List Shuffler")
    
    # Create the text boxes for file names
    micros_textbox = tk.Entry(window, width=50)
    micros_textbox.insert(0, "micros.txt")
    micros_textbox.grid(row = 0, column = 0, pady = 5)
    
    sensors_textbox = tk.Entry(window, width=50)
    sensors_textbox.insert(0, "sensor.txt")
    sensors_textbox.grid(row = 1, column = 0, pady = 5)
    
    inputs_textbox = tk.Entry(window, width=50)
    inputs_textbox.insert(0, "inputs.txt")
    inputs_textbox.grid(row = 2, column = 0, pady = 5)
    
    displays_textbox = tk.Entry(window, width=50)
    displays_textbox.insert(0, "displays.txt")
    displays_textbox.grid(row = 3, column = 0, pady = 5)
    
    # Create the label for the result
    result_label = tk.Text(window, height=10, width=50)
    result_label.grid(row = 0, column = 1,rowspan = 5, pady = 5)
    
    # Define the function to shuffle the lists
    def shuffle_lists():
        # Open the first file and read in its contents
        with open(micros_textbox.get(), "r") as f:
            A = f.read().splitlines()
    
        # Open the second file and read in its contents
        with open(sensors_textbox.get(), "r") as f:
            B = f.read().splitlines()
    
        # Open the third file and read in its contents
        with open(inputs_textbox.get(), "r") as f:
            C = f.read().splitlines()
    
        with open(displays_textbox.get(), "r") as f:
            D = f.read().splitlines()
    
        # Shuffle the lists
        random.shuffle(A)
        random.shuffle(B)
        random.shuffle(C)
        random.shuffle(D)
    
        # Select one item from each list and combine them into a string
        result = A[0] + " + " + B[0] + " + " + C[0] + " + " + D[0] +"\n\n"
    
        # Update the label with the result
        result_label.insert(tk.INSERT,result)
    def generate_combinations():
        # Open the first file and read in its contents
        with open(micros_textbox.get(), "r") as f:
            A = f.read().splitlines()
    
        # Open the second file and read in its contents
        with open(sensors_textbox.get(), "r") as f:
            B = f.read().splitlines()
    
        # Open the third file and read in its contents
        with open(inputs_textbox.get(), "r") as f:
            C = f.read().splitlines()
    
        with open(displays_textbox.get(), "r") as f:
            D = f.read().splitlines()
    
        # Get all the combinations of the lists
        combinations = list(itertools.product(A, B, C, D))
    
        # Write the combinations to a text file
        with open("combinations.txt", "w") as f:
            for combination in combinations:
                f.write(' + '.join(combination) + "\n")
    
        # Show a message box with the number of combinations generated
        messagebox.showinfo("Combinations Generated", f"{len(combinations)} combinations were generated and saved to combinations.txt.")
    
    
    # Create the button to shuffle the lists
    shuffle_button = tk.Button(window, text="Shuffle", command=shuffle_lists)
    shuffle_button.grid(row = 4, column = 0, pady = 5)
    # Add a button to generate the combinations
    generate_button = tk.Button(window, text="Generate Combinations", command=generate_combinations)
    generate_button.grid(row = 5, column = 0, pady = 5)
    
    def open_website():
        webbrowser.open_new("http://www.exasub.com")
    
    link_label = tk.Label(window, text="exasub.com", font=("Arial", 14), fg="blue", cursor="hand2")
    link_label.grid(row=6, column=0, pady=10)
    link_label.bind("<Button-1>", lambda event: open_website())
    # Run the GUI
    window.mainloop()
    
  • How to setup NodeMCU on Arduino IDE

    NodeMCU is an open-source firmware and development board that is based on the ESP8266 Wi-Fi module. It is a popular platform for Internet of Things (IoT) projects due to its low cost, ease of use, and versatility. In this article, we will discuss how to set up NodeMCU on Arduino IDE.

    Step 1: Install Arduino IDE
    The first step is to install the Arduino IDE on your computer. You can download the latest version of Arduino IDE from the official website https://www.arduino.cc/en/software. Choose the appropriate version for your operating system and follow the installation instructions.

    Step 2: Install ESP8266 Board Manager
    After installing the Arduino IDE, you need to install the ESP8266 board manager. This is necessary to add the support for NodeMCU to the Arduino IDE.

    To do this, open the Arduino IDE and go to File > Preferences. In the Additional Boards Manager URLs field, paste the following URL:

    http://arduino.esp8266.com/stable/package_esp8266com_index.json
    

    Click OK and go to Tools > Board > Boards Manager. In the search box, type “esp8266” and select the “esp8266” board by ESP8266 Community. Click Install.

    Step 3: Select the NodeMCU Board
    After the installation of the ESP8266 board manager is complete, go to Tools > Board and select “NodeMCU 1.0 (ESP-12E Module)”.

    Step 4: Select the Port
    Next, you need to select the port to which the NodeMCU is connected. Go to Tools > Port and select the appropriate port.

    Step 5: Upload Your Sketch
    Now, you are ready to upload your sketch to the NodeMCU. In the Arduino IDE, click on File > New to create a new sketch. Write your code and then click on the Upload button to upload the sketch to the NodeMCU.

    Step 6: Verify Upload
    Once the upload is complete, you can verify that the sketch has been successfully uploaded by opening the Serial Monitor. Go to Tools > Serial Monitor and set the baud rate to 115200. If everything is working properly, you should see the output from your sketch in the Serial Monitor.

  • How to setup C/C++ SDK of Raspberry Pi Pico W On Raspberry Pi Model 3b+

    The C/C++ SDK has development tools for both development boards.

    There are various methods of the SDK. You can use this in Windows, MAC etc.
    But the easiest and simplest method is the use of Raspberry Pi itself.

    Step 1: Follow Chapter 1 of the Getting Started with Raspberry Pi Pico https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf

    Step 2: Follow Chapter 8: Creating your own project
    copy the files from pico W folder, which you will find under the pico-examples folder. The blink project will be under the wifi folder.

    Step 3: add the following line to your CMakeLists.text file

    set(PICO_BOARD pico_w)

    Sample CMakeLists.text

    cmake_minimum_required(VERSION 3.13)
    include(pico_sdk_import.cmake)
    project(test_project C CXX ASM)
    set(CMAKE_C_STANDARD 11)
    set(CMAKE_CXX_STANDARD 17)
    set(PICO_BOARD pico_w)
    pico_sdk_init()
    add_executable(test
    test.c
    )
    pico_enable_stdio_usb(test 1)
    pico_enable_stdio_uart(test 0)
    pico_add_extra_outputs(test)
    
    target_link_libraries(test 
    					pico_stdlib
    					pico_cyw43_arch_none
    					)
    

    NOTE: you can set the pico board to pico w. when you issue cmake ..
    Only use one method of setting the pico w board.

    cmake -DPICO_BOARD=pico_w ..
  • How to view PDF in Raspberry Pi model 3b+

    Both Okular and QPDF are great PDF viewers that can be used on a Raspberry Pi Model 3b+. Okular is a graphical application that is easy to use and has a wide range of features, while QPDF is a command-line tool that is lightweight and can be used to quickly view PDF files. Regardless of which tool you choose, both are easy to install and use on your Raspberry Pi Model 3b+.

    Okular

    Okular is a free and open-source document viewer developed by the KDE community. It is known for its ability to handle a wide range of document formats, including PDF, EPUB, and MOBI. To install Okular on your Raspberry Pi Model 3b+, follow these steps:

    1. Open a terminal window on your Raspberry Pi Model 3b+.
    2. Type the following command to update the package list:
    sudo apt-get update
    1. Type the following command to install Okular:
    sudo apt-get install okular

    Once Okular is installed, you can use it to open PDF files by right-clicking on the file and selecting “Open With” -> “Okular”. Alternatively, you can open Okular from the Applications menu and then select “File” -> “Open” to browse for your PDF file.

    QPDF

    QPDF is another popular PDF viewer for Raspberry Pi Model 3b+. It is a command-line tool that can be used to manipulate and view PDF files. To install QPDF on your Raspberry Pi Model 3b+, follow these steps:

    1. Open a terminal window on your Raspberry Pi Model 3b+.
    2. Type the following command to update the package list:
    sudo apt-get update
    1. Type the following command to install QPDF:
    sudo apt-get install qpdf

    Once QPDF is installed, you can use it to view PDF files by typing the following command in the terminal: qpdf –qdf <filename>.pdf

  • Functions in MicroPython on Raspberry Pi Pico

    Functions are a way to make your code more organized and easier to understand. They are like little machines that you can use over and over again in your code.

    To create a function, you need to give it a name and then write what it does. You can also give it some inputs, like numbers or strings, that it will use to do its job.

    For example, imagine you wanted to make a function that adds two numbers together. You could call it “add” and write the code like this:

    def add(num1, num2):
        result = num1 + num2
        return result

    Now, whenever you want to add two numbers together, you can just call the “add” function and give it the two numbers you want to add:

    result = add(5, 7)

    The “add” function will take those two numbers, add them together, and then return the result, which you can store in a variable called “result”.

    Functions are really helpful because they can make your code shorter, easier to read, and easier to test.

    Examples to try

    Here are some more examples of functions in MicroPython on Raspberry Pi Pico:

    Example 1: Adding Two Numbers

    def add_numbers(a, b):
        result = a + b
        return result
    
    # Test the function
    print(add_numbers(2, 3)) # Output: 5
    print(add_numbers(5, 10)) # Output: 15

    Example 2: Counting Even Numbers

    def count_even_numbers(numbers):
        count = 0
        for num in numbers:
            if num % 2 == 0:
                count += 1
        return count
    
    # Test the function
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    print(count_even_numbers(numbers)) # Output: 5

    Example 3: Finding the Maximum Number in a List

    def find_max(numbers):
        max_num = numbers[0]
        for num in numbers:
            if num > max_num:
                max_num = num
        return max_num
    
    # Test the function
    numbers = [3, 9, 2, 5, 1, 8, 4, 7, 6]
    print(find_max(numbers)) # Output: 9
  • How to interface 0.96″ OLED display with Raspberry Pi Pico without library using I2C in Micropython

    The 0.96-inch OLED screen is monochromatic blue. Which means it has only blue light. You can either switch on the led to make the text blue or you can invert the in which background is blue and the text black.

    The OLED uses a ssd1306 IC. Which is a 128 x 64 Dot Matrix OLED/PLED Segment/Common Driver with Controller.

    The code written does not need any library to be installed.

    I made the following connections

    OLED		RPI Pico
    
    SDA	->	RP0
    SCK	->	RP1
    VDD	->	3v3
    GND	->	GND
    		
    Note: RP0 and RP1 are pin number 0 and 1 on the pico develeopment board.

    Code

    # MicroPython SSD1306 OLED driver, I2C and SPI interfaces
    import machine
    import time
    import framebuf
    import utime
    
    # register definitions
    SET_CONTRAST        = const(0x81)
    SET_ENTIRE_ON       = const(0xa4)
    SET_NORM_INV        = const(0xa6)
    SET_DISP            = const(0xae)
    SET_MEM_ADDR        = const(0x20)
    SET_COL_ADDR        = const(0x21)
    SET_PAGE_ADDR       = const(0x22)
    SET_DISP_START_LINE = const(0x40)
    SET_SEG_REMAP       = const(0xa0)
    SET_MUX_RATIO       = const(0xa8)
    SET_COM_OUT_DIR     = const(0xc0)
    SET_DISP_OFFSET     = const(0xd3)
    SET_COM_PIN_CFG     = const(0xda)
    SET_DISP_CLK_DIV    = const(0xd5)
    SET_PRECHARGE       = const(0xd9)
    SET_VCOM_DESEL      = const(0xdb)
    SET_CHARGE_PUMP     = const(0x8d)
    
    
    class SSD1306:
        def __init__(self, width, height, external_vcc):
            self.width = width
            self.height = height
            self.external_vcc = external_vcc
            self.pages = self.height // 8
            # Note the subclass must initialize self.framebuf to a framebuffer.
            # This is necessary because the underlying data buffer is different
            # between I2C and SPI implementations (I2C needs an extra byte).
            self.poweron()
            self.init_display()
    
        def init_display(self):
            for cmd in (
                SET_DISP | 0x00, # off
                # address setting
                SET_MEM_ADDR, 0x00, # horizontal
                # resolution and layout
                SET_DISP_START_LINE | 0x00,
                SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
                SET_MUX_RATIO, self.height - 1,
                SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
                SET_DISP_OFFSET, 0x00,
                SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
                # timing and driving scheme
                SET_DISP_CLK_DIV, 0x80,
                SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
                SET_VCOM_DESEL, 0x30, # 0.83*Vcc
                # display
                SET_CONTRAST, 0xff, # maximum
                SET_ENTIRE_ON, # output follows RAM contents
                SET_NORM_INV, # not inverted
                # charge pump
                SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
                SET_DISP | 0x01): # on
                self.write_cmd(cmd)
            self.fill(0)
            self.show()
    
        def poweroff(self):
            self.write_cmd(SET_DISP | 0x00)
    
        def contrast(self, contrast):
            self.write_cmd(SET_CONTRAST)
            self.write_cmd(contrast)
    
        def invert(self, invert):
            self.write_cmd(SET_NORM_INV | (invert & 1))
    
        def show(self):
            x0 = 0
            x1 = self.width - 1
            if self.width == 64:
                # displays with width of 64 pixels are shifted by 32
                x0 += 32
                x1 += 32
            self.write_cmd(SET_COL_ADDR)
            self.write_cmd(x0)
            self.write_cmd(x1)
            self.write_cmd(SET_PAGE_ADDR)
            self.write_cmd(0)
            self.write_cmd(self.pages - 1)
            self.write_framebuf()
    
        def fill(self, col):
            self.framebuf.fill(col)
    
        def pixel(self, x, y, col):
            self.framebuf.pixel(x, y, col)
    
        def scroll(self, dx, dy):
            self.framebuf.scroll(dx, dy)
    
        def text(self, string, x, y, col=1):
            self.framebuf.text(string, x, y, col)
    
    
    class SSD1306_I2C(SSD1306):
        def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
            self.i2c = i2c
            self.addr = addr
            self.temp = bytearray(2)
            # Add an extra byte to the data buffer to hold an I2C data/command byte
            # to use hardware-compatible I2C transactions.  A memoryview of the
            # buffer is used to mask this byte from the framebuffer operations
            # (without a major memory hit as memoryview doesn't copy to a separate
            # buffer).
            self.buffer = bytearray(((height // 8) * width) + 1)
            self.buffer[0] = 0x40  # Set first byte of data buffer to Co=0, D/C=1
            self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height)
            super().__init__(width, height, external_vcc)
    
        def write_cmd(self, cmd):
            self.temp[0] = 0x80 # Co=1, D/C#=0
            self.temp[1] = cmd
            self.i2c.writeto(self.addr, self.temp)
    
        def write_framebuf(self):
            # Blast out the frame buffer using a single I2C transaction to support
            # hardware I2C interfaces.
            self.i2c.writeto(self.addr, self.buffer)
    
        def poweron(self):
            pass
    
    
    
    
    
    
    
    i2c = machine.SoftI2C(scl=machine.Pin(1), sda=machine.Pin(0))
    
    pin = machine.Pin(16, machine.Pin.OUT)
    pin.value(0) #set GPIO16 low to reset OLED
    pin.value(1) #while OLED is running, must set GPIO16 in high
    
    oled_width = 128
    oled_height = 64
    oled = SSD1306_I2C(oled_width, oled_height, i2c)
    
    oled.fill(0)
    oled.text('hallo, ', 0, 0)
    oled.text('exasub.com!', 0, 10)
    def toggle(i):
        if i == 0:
            i = 1
        else:
            i = 0
        return i
    
    i=0
    
    while True:
        i = toggle(i)
        oled.invert(i)
        oled.show()
        utime.sleep(1)