Welcome to the fourth lesson of this python course. Our previous session taught us how to utilize the print function in python, so we have a firm grasp of the terminology and the functions themselves. In this lesson, we'll cover a few more Python terms, such as:
Also, we'll build a simple program to print out an imagined dog so that we may better grasp how these concepts are employed. So, let's get started now.
Programming is a lot like building a structure out of blocks. Even with just a few types of children's toy blocks and some time and imagination, you can build anything. Because we'll be utilizing these phrases all the time in programming, it's critical that you know what they mean and how to use them.
An alphabet, word, or other character collection is referred to as a "string." As one of the most fundamental data structures, it serves as a framework for manipulating data. An in-built string class called "str" is available in Python. After they've been produced, strings are "immutable," which means that they can't be rewritten. Because of the immutability of strings, we must generate new ones each time we want to represent newly computed values.
Quotes are used to denote a string. There are a variety of ways to accomplish this:
"Double quotes allow you to embed 'single' quotes in your string."
Triple quoted strings to make it possible to work with a set of multiple-line strings and include all of the whitespaces that accompany them.
The fact that a string cannot be changed results in an error if you try to do so. The adjustments require the creation of a new string.
Instead, use this method.
The built-in len() function can be used to determine the length of a string:
Strings can be sliced and indexed since they are a sequence of characters. A string's indexing starts at 0 and is based on each character in the string.
The initial character in the string is C, which is located at position 0 of the index. The final syllable is a period, which is the string's sixteenth character. When you want to access characters in the opposite direction, you can use -1 as an index. when it's strung together, Chocolate and cookie are separated by a whitespace, which has its own index, 9 in this example. Slicing is a good way to verify this.
For the same reason as for other sequential data types, you can read and manipulate Python strings using their associated index numbers. It is possible to slice an object using its index values in Python to select a specific element or a subset of elements. You don't have to write a loop expression to identify or access specific substrings in a string. Slicing does this for you automatically.
Suppose you were trying to find the cookie substring in the following string. What's the best way to go about it?
Range slicing is used in these situations. The range slicing syntax is as follows:
Alternatively, you might use a negative stop index:
In this case, when you slice a sentence without giving an end index, you get characters from the first index to its last. In the same way, slicing a string without a starting index indicates that you begin at the beginning and end at the end.
Additionally, the stride parameter can be accepted by string-slicing as a third argument, which specifies the number of characters to advance once the initial one is picked from the string. In the default configuration, stride has a value of 1.
stringnu = "1020304050"
print (stringnu [0:-2:2])
Striding allows you to reverse a string, which is a really cool feature. With a stride of -1, you can begin at the end of the string and move forward one character at a time. With a value of -2, you can start at the end and move two characters at the same time.
String operations such as slicing and range slicing are frequent. As simple as adding, string concatenation is also available.
Concatenating a string with another data type, on the other hand, will fail.
You attempted to concatenate an integer value with a string, which is not permitted. Integer addition or string concatenation is not understood implicitly by the interpreter. However, give this a try:
The reason for this is that you used concatenation after you turned the integer into a string.
A string can be repeated using the * method.
wordsig = 'hip '
line1 = wordsig * 2 + 'hurray! '
print (line1 * 3)
To manipulate strings, Python comes with several built-in methods and utility functions. It is possible to use these built-in techniques to replace substrings, to put some words in a paragraph in capital letters, and to locate the position of a string within another text.
Multiple string formatting options are available in Python. To better understand these formatting strings, let`s dive right in.
Python has a built-in modulo percent operation. The interpolation operator is the name given to it. There is a percent followed by the data type that must be prepared or transformed. This operation then replaces the word "percent datatype" with one or more components of that type:
Percent d is used for integers, whereas percent s is used for strings; you've seen both. Octal values can be converted to octal equivalents with this type of conversion, as can Hexadecimal values with this type, and Floating-Point Decimal Format with this type.
One of the built-in string classes is the formatter class. The format () method can be used to perform sophisticated variable substitutions and value formatting. Rewriting public methods such as format () and vformat () allows you to build your own string formatting techniques (). There are a number of methods that are designed to be replaced by subclasses, such as parse (), get field, get value, check unused arguments, format field, and convert field ().
Templates allow substitutions based on dollars rather than percentages. A major reason for template syntax creation in Python Version 2.4 was that, despite the strength of percent string formatting, errors are easy to make, because the forms that follow '%'are extremely restrictive. This is a common blunder when it comes to percent formatting: forgetting to include the e in percent (variable).
substitution () and safe_substitute() are two methods specified within templates (). You can use them in the following ways:
Safe substitution () is an advantage to employing a template, in addition to other advantages.
In Python 3, this is yet another way to format strings. A prefixed 'f' or 'F' string literal is known as a formatted string literal or f-string. Within curly brackets, you may include identifiers that will be utilized in your string.
What's the point of adding another string formatting option? well, this is because practicality and simplicity are appealing.
To demonstrate why f-strings are the best way to format strings in Python, check out the examples below.
Please note that the preceding code is only compatible with Python 3.6 and above. With f-strings, Python expressions can be used inside curly braces, which is a significant benefit of using them.
Syntax is the most important consideration here. For the most part, it boils down to the trade-off between simplicity and the amount of verbosity you're willing to sacrifice. People with a C programming background will find it easy to use the percent sign to format strings, for example. Using the format () function can be more verbose, but it provides a wider range of options.
While your application is running, you can utilize input routines to get data from the user. A key benefit of this approach is that it does not rely on preexisting values or file content to function. The syntax for the input function is as follows.
input([prompt])
Input functions will cause our application to pause. After the user inserts the text into the Python shell or command line, the application resumes.
input(message)
In order to prompt the user for text, you'll need to provide a message. It's important that a user understands what they need to do by reading this message. As a result, a user may wonder why the software isn't progressing. For example,
input ("Enter email address: ")
print ("Confirm it is your email address:")
In order to request an email address from a user, we've implemented the input () method. Messages in brackets are displayed on the same line where a user is expected to enter text in the command line.
Note that as soon as a user inputs data into the input () function, it is automatically converted to a string.
Using the fundamentals of strings that we've learned in this lesson; we'll construct a simple program that prints out an image of a dog.
Let's open up our favorite coding editor, Atom, and get started. Before looking at the solution, I advise you to give it a shot on your own.
Congratulations! You've made it this far. You have learned about string slicing, what strings are, and explored a variety of string-related processes. Many approaches to formatting strings have also been discussed. But don't forget that practice is the key to mastering any skill! I'll see you in the next tutorial.
The more you learn about Python, the more you may use it for your own purposes. Data analyst, application developer, or the ability to automate your work processes are all examples of jobs that can be automated.
This Python 3 tutorial will show you how to create a simple "Hello, World" program. Python's basic syntax and components include the following:
An IDE (Integrated Development Environment) is a software development tool. Integrated development environments (IDEs) include a variety of software development-specific tools. Examples of these instruments include:
There are many distinct programming languages supported by IDEs, as well as a wide range of additional functionality. Because of this, they can be very huge and take a long time to download and install. Using them correctly may necessitate additional training.
A function is a piece of code that serves a single purpose and can be reused multiple times. Functions provide for greater modularity and code reuse in your program. Functions have the benefit of being well-known by a variety of names. Functions, methods, subroutines, procedures, etc. are all referred to in different ways by different programming languages. Think about what we'll be talking about later in this session if you come across any of these terms.
Since you all learned Python by printing Hello, World! you might think that there is nothing new to learn about the Python Print function. As with any language, learning to use the Print function in Python or any other is like taking your first baby steps into a new world of programming. When studying a programming language, it's easy to get caught up in the more advanced concepts and lose sight of the simplicity and usefulness of basic functions.
Today's tutorial is all about Python's Print function; you'll learn about one of the most underappreciated functions.
For example, in Python3, parenthesis is required or else you'll get a syntax error as illustrated in the image below.
In Python3, print () is not a statement but a function, as demonstrated by the above output. First things first, let's see what the print () function returns.
Built-in functions and methods are returned by this method, which indicates that it is a Python function.
A new line or vertical space between the two outputs can be added by simply using the print () function without sending any arguments in.
The Command Palette, which is possibly Atom's most essential command, is shown to us on that welcome page. The command palette will appear if you press Ctrl+Shift+P while in an editor pane.
Packages from the Atom community are available to help you assemble and run programs. We'll be utilizing "script" to run our application in this example.
go to file>settings>install
Install script by searching for it in the search bar. It should appear under "Packages" in the Settings menu after installation. Please be aware that script does not support human input. The "apm" package manager can be used to install packages on Mac OS or Linux.
Go to File > Add Project Folder in atom and pick a directory to serve as the project's root directory.
In the folder, right-click the folder and select "New File," type "hello."py," and click "OK."
Now that you've made your adjustments, you can open the new file in the editor by clicking on it and then saving it.
Then, in the Print dialog box, type "hello, world!"
To execute the script, use CTRL+SHIFT+B. You may also use View > Toggle Command Palette and type Script: Run to execute a script.
You can also use your terminal to run the python file by navigating to the file directory containing your hello.py file and running this command
python hello.py
File editing is rather simple. You can use your mouse and keyboard to navigate and edit the content of the page. A separate editing mode or key commands are not provided. Take a look at the list of Atom packages if you prefer editors that have modes or more advanced key commands. Many programs are available that mimic popular design elements.
You may save a file by selecting File > Save from the menu bar or by pressing Ctrl+S. There are two ways to save the current material in your editor: by selecting File > Save As or using Ctrl+Shift+S. Finally, you can save all open files in Atom by selecting File > Save All.
The majority of your time will be spent working on projects with numerous files, not just single files. Take advantage of the File > Open Folder menu option and select an appropriate folder from the drop-down menu. File > Add Project Folder or hitting Ctrl+Shift+A can also be used to add other directories to your current Atom window.
The command line utility, atom, allows you to open unlimited number of directories by supplying their paths to it. The command atom./hopes./dreams, for example, can be used to simultaneously open the hopes and dreams directories.
An automated Tree View will be displayed on the side of Atom if one or more directories are open.
When you use the Tree View, it's a breeze to see the whole file and directory structure of your project. You can open, rename, and delete files, as well as create new ones, using this window.
In order to toggle between concealing and showing it, use Ctrl+, use the tree-view: toggle command from the Menu Bar, or press Alt+ to bring focus to it. The A, M, and Delete keys can be used to add, move, or remove files and directories in the Tree view. It's also possible to access these choices by right-clicking on a file or folder in the Tree view, as well as copying or pasting its path into your clipboard.
Unlike functional programming languages that used a single long list of instructions, Python uses code modules that may be switched out. Cpython is the default Python implementation. It is the most often used Python implementation.
Python does not translate its code into a form that hardware can understand, known as machine code. As a result, it turns it into byte code. Python does have a compiler, but it doesn't compile to a machine language. CPUs are unable to decode the byte code (.pyc or.pyo). We'll run the bytes through Python's virtual machine interpreter.
To convert a script into an executable, the Python source code follows these steps:
First, the python compiler reads a python source code or instruction from the command line. It ensures proper formatting of the instruction by inspecting the grammar of each line. The translation is immediately interrupted if an error is found, and an error message is presented.
Assuming there are no errors and the Python source code or instructions are properly formatted, the compiler proceeds to translate them into a form known as "Byte code," which is an intermediate language.
The Python interpreter is invoked by executing bytes of code in the Python Virtual Machine (PVM). PVM is a Python virtual machine (PVM) that turns bytecode into machine code. If there is a problem with the interpretation, the conversion will be interrupted and an error notice will be displayed.
Congratulations for completing your first program. Beginners who want to learn Python can benefit greatly from this guide. To get the most out of this lesson, you may want to play around with the Print function a little more and discover more features that were not covered.
The first step to becoming a Python coder is to install or update Python on your computer. Python can be installed in a variety of ways, including through the official Python.org distribution, a software package manager, the IoT (Internet of Things) and scientific computing, just to name a few.
In this article, we'll be using official Python distributions, which are often the best option for beginners.
Installing the most recent versions of Python and the packages you'll need to experiment with is a good place to start when learning Python. To create an environment, you need a certain Python version, as well as the necessary packages. Consequently, separate environments are required if you wish to create or utilize apps that have varied Python or package version needs.
Python's virtual environment is a valuable tool for managing dependencies and separating projects. It's possible to install Python site packages (third-party libraries) in a specific project directory rather than the entire system Python.
On Windows, there are three installation options:
You'll learn how to check the current release of Python installed on your Windows machine in this section. You'll also discover which of the three installation options is best for you.
Step 1: Install Python based on your choice of version.
Python 2 and Python 3 are available, each with its syntax and way of doing things.
Here we are going to download python 3 for this course.
Step 2: Download an executable installation file for Python.
Open on your browser and head to the python.org website. On this page click on downloads. Here you can find the latest version of python. Choose the version you require and click Download. For this example, we go with Python 3.10.2.
When you select download, a list of available executable installers with varied operating system requirements will appear. Select the installer that best suits your system's operating system and download it. Let's say we go with the Windows installer (64 bits).
Step 3: Run the Installer Script
If the Python 3.10.2 Windows 64-bit installation was downloaded, run the installation program by double clicking it. Make sure both checkboxes at the bottom are selected before clicking Install New.
Now installation process begins when you click the Install Now button. Wait for a few minutes for installation process to finish.
you should see the screen below if the installation is complete. Now you have python installed in your computer.
Step 4: On Windows, check to see if Python is installed.
To see if Python has been installed successfully on your system. Observe the instructions.
When installing python in Linux distros, there are two ways involved:
You'll find out how to know if your Linux computer has a current version of Python in this section and which of the two installation techniques should you choose?
Many Linux versions include Python, but it is unlikely to be the most recent update, and it may even be Python 2 rather than Python 3. Try the following command in a terminal window:
$ python –version
If you have Python installed on your computer, this command should return a version number.
If your current Python version isn't the most recent Python 3 version available, you'll want to upgrade.
Step 1: Installing Python requires first downloading and installing the necessary development packages
A new version of Debian has been released; therefore, we need to update our old version to the new one.
Open the terminal in your Linux machine. Then run “apt update” in your Linux terminal to update the system before you begin installing python. Then, run "apt-get upgrade" in your terminal to upgrade the system.
then, run " apt install build-essential zlib1g-dev \libncurses5-dev libgdbm-dev libnss3-dev \libssl-dev libreadline-dev libffi-dev curl" to install the build essentials.
Step 2: Download the most recent version
Navigate to your browser in python.org and click on downloads. You will see the latest version of python with a download button, but this is the windows version, instead, navigate to the Linux/UNIX link below it to download the Linux version.
Download the most recent version of Python3 from the official Python website.
You will receive an archive file ("tarball") containing Python's source code when the download is complete.
Step 3: Unzip the tarball folder to a convenient location.
A tar.gz file is a collection of compressed files that may be downloaded in order to conserve space and bandwidth. The tarball, another name for the.tar file, is a container for other files that may be carried about on a flash drive. Because of the extension, gzip is the most extensively used compression application in use. These files can be unzipped in the same way as a standard zipped file:
Run “tar –xvzf a.tar.gz” in the terminal to unzip.
Step 4: The downloaded script must be configured.
Type cd Python-3.*. /configure in your terminal once you've unzipped the Python package and press enter.
Step 5: Begin the build procedure
Use this command below if you need to install an updated version alongside the old version in case you don`t want to delete the old one:
$ sudo make install
Step 6: Verify that the installation is working properly.
Open your terminal and type command below and click enter.
python --version
Python3 has been successfully installed once the output says Python 3.x.
Congratulations! For your system, now you have access to the most recent update of Python. Your Python adventure is just getting started.
Greetings! I sincerely hope everything is going well for you all. In this course, we are going to learn step-by-step how to program in Python. The course covers all you need to know about the Python language, from installation to advanced topics. In addition, we'll talk about Python career jobs and do a few projects to strengthen your skills. According to my research, Python is among the top programming languages in use today. (I mean, no offense). Since I am also a Python programmer, I may sound a little prejudiced, but I can certainly declare that I am a huge fan of the language. This tutorial series is meant for absolute beginners with no prior knowledge of python programming, it is also of great help for experienced python programmers looking to brush up on their knowledge. Anyway, let’s start by answering a few questions:
Python is a high-level scripting language for system administration, text processing, and web tasks. Its core language is short and easy to learn, while modules can be added to do a virtually infinite number of functions. It is a genuine object-oriented programming language that runs on a wide range of systems. Python was born out of a desire to build a computer language that was both easy to learn for beginners and powerful enough for advanced users. Python's small, crisp syntax reflects this background, as does the thoroughness with which notions are implemented, without removing the flexibility to program in a more traditional approach. As a result, Python is a fantastic first programming language that provides all of the power and advanced capabilities that users will require in the future. Most experienced programmers claim that Python has restored the fun they normally had during programming, suggesting that van Rossum's inspiration is adequately conveyed in the language itself.
Even those, who aren't computer programmers, have found themselves using it, to perform mundane tasks like raising money because of its relatively low learning curve.
Building a system that is easy to maintain and update requires careful consideration of the quality of the program code. Because of the language's syntactic rules, you may express yourself in Python without writing any more code. With Python, you may use English phrases instead of punctuation, which makes it easier to understand than other computer languages. You don't have to write any more code when using Python to create custom apps. If the code is well-structured, it will make it easier to keep and improve the product.
Code written in this programming language may be executed on a variety of systems and tools thanks to interpreters for this language. When it comes to creating dynamic web pages, Python is also an interpreted programming language. '" There's no need to recompile the application to operate on many operating systems. As a result, changing the code doesn't require recompilation. You don't need to recompile the updated application code to see how the changes affect it. Code updates may be made more rapidly and without increasing development time by using this feature.
Python outweighs other languages because of its vast and robust standard library. You can select from a wide choice of modules in the standard library to meet your specific requirements. If you're building web apps, dealing with operating system interfaces, or working with internet protocols, you can make use of specific packages. The documentation for the Python Standard Library can also help you learn about different modules.
As an open-source coding language, Python can lower software development costs significantly. Some of the Python modules, libraries, and development tools, all of which are open-source and free, may help you get things done faster. Open-source Python development tools are also available, so you may tailor your project to your exact needs. Flask, Pyramid, and Cherrypy are some of the best Python frameworks for web development.
Python may be used to build both desktop and web-based applications. Python may also be used to create complex scientific and numerical applications. Easy-to-use Python capabilities make it possible to perform data analysis and visualizations. Data analysis tools can be used to construct custom big data solutions without requiring additional effort or time. You may make your collected data more visually attractive and useful by utilizing its data visualization tools and APIs. Many Python programmers also use Python for AI and NLP jobs.
This programming language is used for cloud computing, web and software development, as well as task automation and data analysis.
Since everything is stored in the cloud, you may access it at any time and from anywhere. Web-based software may be used without any installation, an application can be hosted online, and a remote file storage and database system built using cloud computing can be set up. For that purpose, we have different modules like raspberry pi and ESP32 or ESP8266 boards which use python.
The ESP32 is the latest sibling of the ESP8266, which is a microcontroller. For a nominal additional fee, it boosts the device's power and capabilities while also including Bluetooth. The M5 Stack is one of the greatest iterations of these boards. Piezo speakers, batteries, a card reader, and a color screen are all included in this device.
Python has a big and active community of developers that respond to concerns and build new features at a pace that many commercial software developers would consider exceptional (if not downright shocking). A source-control system is used by Python programmers to coordinate their work remotely. The PEP (Python Enhancement Proposal) procedure must be followed and must be accompanied by improvements to Python's sophisticated regression testing infrastructure for any changes. A far cry from the early days of Python, when an email to the author would suffice, altering Python nowadays is about as complicated as upgrading commercial software. This is a good thing given Python's present vast user base.
Python is installed on Windows in a few simple procedures.
Step 1: Install Python based on your choice of version.
Python 2 and Python 3 are available, each with its syntax and way of doing things.
Here we are going to download python 3 for this course.
Step 2: Download an executable installation file for Python.
Use browser to Navigate to the Download for Windows area of the official Python website.
Choose the version you require and click Download. For example, I go with Python 3.9.1.
When you select download, a list of available executable installers with varied operating system requirements will appear. Select the installer that best suits your system's operating system and download it. Let's say we go with the Windows installer (64 bits).
Step 3: Run the Installer Script
The Python 3.9.1 Windows 64-bit installation was downloaded.
Run the installation program. Make sure both checkboxes at the bottom are selected before clicking Install New.
Now installation process begins when you click the Install Now button.
A few minutes after starting the installation process, you should see the screen below.
Step 4: On Windows, check to see if Python is installed.
To see if Python has been installed successfully on your system. Observe the instructions.
Step 1: Installing Python requires first downloading and installing the necessary development packages.
A new version of Debian has been released.
Run "apt-get upgrade" in your terminal.
then, "libssl-dev libreadline-dev libffi-dev curl"
Step 2: Download the most recent version.
Python has several versions that are available on their website.
Download the most recent version of Python3 from the official Python website. You will receive a.tar.xz archive file (a "py") containing Python's source code when the download is complete.
Step 3: Unzip the py folder to a convenient location.
To extract the file, either use an extractor program or the Linux tar command.
Step 4: The downloaded script must be configured.
Type cd Python-3.*. /configure in your terminal once you've unzipped the Python package and run it
Step 5: Begin the build procedure
Use this command below if you want to install the new version alongside the old version in case you don`t want to delete it:
$ sudo make install
Step 6: Verify that the installation is working properly.
To test it, type the following command into your terminal:
python3 --version
Python 3 has been successfully installed once the output says Python 3.x.
PWM stands for Pulse-Width Modulation. Once the switching frequency (fsw) has been chosen, the ratio between the switch-on time (TON) and the switch-off time (TOFF) is varied. This is commonly called duty-cycle (D). The duty cycle can be between 0 and 1 and is generally expressed as a percentage (%).
D = TON / (TON + TOFF) = TON x fsw
The variation of the pulse width, made at a high frequency (kHz), is perceived as continuous and can be translated into a variation of the rotation speed of a motor, dimming a LED, driving an encoder, driving power conversion, and etc. The use of PWM is also widely used in the automotive sector in electronic control units (ECU - Electronic Control Unit) to manage the energy to be supplied to some actuators, both fixed and variable frequency. Among the various actuators used in a vehicle and controlled by PWM i.e. diesel pressure regulator, EGR valve actuator, voltage regulator in modern alternators, turbocharger variable geometry regulation actuator, electric fans, etc.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | STM32 Nucleo | Amazon | Buy Now |
In this article, we will see how to configure and write an application to dim an LED connected to an output pin (GPIO) of the STM32F446RE Nucleo board. First of all, let's see how to configure the initialization code with the STCube tool.
It is necessary to identify a GPIO to associate a timer to generate the PWM. For example, we choose PB3 associated with Channel 2 of Timer 2 (TIM2_CH2). Note that the pin turns orange to remind us of that timer 2 still needs to be configured.
Now you need to configure timer two which is hooked to pin PB3.
We configure the clock tree so that the clock frequency (HCLK) of the microcontroller is 84 MHz, handling frequency divisor and multiplier of PLLCLK. The bus to which the timers are connected (APB1 timer and APB2 timer clocks) is also at 84 MHz.
We now know that timer 2 has a clock source of 84 MHz, so every time the clock switches the timer does a count. If we configure it to count to 84 million (it is a 32-bit timer, the Counter must be between 0 and 4294967295) it will take a second. We can set this number to exactly half so that the LED lights up for half a second and turns it off for another half-second. In practice, we will see at the output a square wave that will have a duty cycle of 50% and a switching frequency of 1Hz (too slow not to notice the switching on and off the LED !!). To do what has been said, the timer must be configured as follows:
Now we are ready to lunch the initialization code and write our application.
/* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM2_Init(void);/* USER CODE BEGIN PFP */At the end of main() we find the initialization code of System Clock, Timer 2 and GPIO ( as previously selected in STCube):
void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 16; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; RCC_OscInitStruct.PLL.PLLQ = 2; RCC_OscInitStruct.PLL.PLLR = 2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /** * @brief TIM2 Initialization Function * @param None * @retval None */ static void MX_TIM2_Init(void) { /* USER CODE BEGIN TIM2_Init 0 */ /* USER CODE END TIM2_Init 0 */ TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; /* USER CODE BEGIN TIM2_Init 1 */ /* USER CODE END TIM2_Init 1 */ htim2.Instance = TIM2; htim2.Init.Prescaler = 0; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 83999999; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM2_Init 2 */ /* USER CODE END TIM2_Init 2 */ HAL_TIM_MspPostInit(&htim2); } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOB_CLK_ENABLE(); } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ }
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM2_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 41999999); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
As mentioned earlier we have set the timer two to count up to 84 million, so we know that counting 84 million a second has elapsed. Now let's use the PWM function to make sure that for half a second the LED stays on and the other half a second off. In practice, to generate a square wave with a duty cycle of 50% and a frequency of one second.
We use the function “HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2)” to enable timer 2 to start in PWM mode and the macro “__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 41999999)” tells the timer which is the value with which to compare the internal count (in this case 41999999) to decide whether the LED should be off or on.
Now we just must connect a led between pin PB3 and ground through a 220OHm resistor to limit the current (if we want to work with a duty cycle of 100%) and compile our application.
Once this is done, we see that the LED will remain on for 500ms second and off for another 500ms as can be seen from the waveform acquired by the PB3.
First, we declare the following define:
#define D_COUNTER 839
Now, we associate it with the field “htim2.Init.Period” of the structure *htim2:
htim2.Init.Period = D_COUNTER;
In this way, we can quickly the number of counts that the timer must do and therefore manage the frequency and duty cycle of our PWM.
This way our timer will count up to 839 in 10us. Consequently, the switching frequency will be 100kHz (clearly exceeding 1Hz !!). Note that as in the previous example we have subtracted 1 from the count value because the timer starts at zero.
Then, we define an unsigned int variable to set the duty cycle:
unsigned int D; //duty cycle In the main() we write; /* USER CODE BEGIN 2 */ D= 10; //it menas duty cycle of 10% HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, (D_COUNTER/100)*D); /* USER CODE END 2 */
Where (D_COUNTER/100)*D needs to re-proportion the value of the duty cycle to the count that the timer must perform.
Compiling we will see that now the LED will always be on but with low brightness, in fact, the duty cycle is only 10% as can be seen from the generated waveform and the rms value of the voltage given to the LED is about 1.1 Volt (as you can see in the bottom corner of the RMS figure for the green trace). Furthermore, the figure confirms that the duty cycle is 10% both intuitively by looking at the green trace and by reading the measurement at the bottom center (Dty+ = 10,48%).
If we set D=70, the LED will be brighter, in fact the RMS value is about 2.82 Volt (as you can see in the bottom corner of the RMS figure for the green trace). Furthermore, the figure confirms that the duty cycle is 70% both intuitively by looking at the green trace and by reading the measurement at the bottom center (Dty+ = 69,54%).
If we set D=100, the led will be illuminated with the maximum brightness imposed by the limitation of the 220 Ohm resistor. The rms value at the ends of the LED with the resistance in series will be 3.3 Volts (the maximum generated by the GPIO)
Now if you write the following code on while(), we will see that the LED will change brightness every 100ms (in practice it increases, every 100ms the duty by 1% starting from 1% until it reaches 100% and then starts all over again)
/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ for(D=1; D<=100; D++){ HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, (D_COUNTER/100)*D); HAL_Delay(100); } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
To do this we include the PWM configuration function ( HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, (D_COUNTER/100)*Duty) in the following loop for(D=1; D<=100; D++) which increases the value of variable D by 1 every 100 ms through the function HAL_Delay(100).
Now we are ready to effectively manage the PWM to control the brightness of a led, but in a similar way, we can control the speed of a DC motor or various actuators.
One of the simplest and most classic DACs is the R-2R ladder, but today there are more complex objects with optimization in the signal reconstruction. Below is shown a 3bit R-2R ladder DAC.
Vout= -1/2*B1*Vref - (1/4)*B1*Vref- (1/8)*B1*Vref
If the 3bit string is D and Vref is equal to the logical voltage of 3.3V
Vout= (3.3*D)/2^3
The typical output characteristic is shown in the following figure.
Compared to the weighted resistor DAC, the R-2R scale DAC has the advantage of using only two resistive values. Therefore, it is more easily achievable with integrated circuit technology.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | STM32 Nucleo | Amazon | Buy Now |
Many of the STM32 microcontrollers have on board at least one DAC (generally 12 bit) with the possibility of buffering the output signal (with internal operational amplifier OP-AMP). The use of the DAC finds various applications, for example, it can be used to reconstruct a sampled signal or to generate any waveform (sine wave, square wave, sawtooth, etc.), to generate a reference voltage (for example for a digital comparator).
The DAC peripheral can be controlled in two ways:
In this modality we can drive DAC to on/off a LED, to generate a reference voltage, etc. We will use a NUCLEO STM32L053R8 board to show as configure DAC with STCube. This NUCLEO has available a DAC with only one channel (in general every DAC has one or more channels) with resolution up to 12bit with a maximum bus speed of 32 MHz and a maximum sampling rate of 4 Msps. First, let's see how to initialize the peripherals using STCube Tool:
At this point, let's look at the generated code:
typedef struct { DAC_TypeDef *Instance; /*!< Register base address */ __IO HAL_DAC_StateTypeDef State; /*!< DAC communication state */ HAL_LockTypeDefLock; /*!< DAC locking object */ DMA_HandleTypeDef *DMA_Handle1; /*!< Pointer DMA handler for channel 1 */ #if defined (DAC_CHANNEL2_SUPPORT) DMA_HandleTypeDef *DMA_Handle2; /*!< Pointer DMA handler for channel 2 */ #endif __IO uint32_t ErrorCode; /*!< DAC Error code }DAC_HandleTypeDef;
/* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DAC_Init(void); /* USER CODE BEGIN PFP */
Now, before writing our application, let's see what functions the HAL library makes available to handle the DAC.
The voltage output will be:
Vout,DAC = (Vref*data)/2^nb
where nb is a resolution (in our case 12bit), Vref is voltage reference (in our case 2 Volt) and the passed data.
So, to set DAC output to 1 Volt data must be 2047 (in Hexadecimal 0x7FF) so we call the function this way:
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 0x7FF);
To set the output voltage to 3.3 Volt we call function in this way:
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 0xFFF);
To verify that change the value in our main we write the following code and then we check the output connecting it to the oscilloscope probe.
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DAC_Init(); while (1) { /* USER CODE END WHILE */ HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 0x7FF); HAL_Delay(1000); HAL_DAC_Stop(&hdac, DAC_CHANNEL_1); HAL_Delay(1000); HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 0x7FF); HAL_Delay(1000); HAL_DAC_Stop(&hdac, DAC_CHANNEL_1); HAL_Delay(1000); /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
We expect the output voltage to change every second by repeating the following sequence: 1V - 0V - 2V – floating (as shown in the figure below)
Let's see mathematically how to reconstruct a sinewave starting from a given number of samples. The greater the number of samples, the more "faithful" the reconstructed signal will be. So, the sampling step is 2pi / ns where ns is the number of samples in this way, we have to save our samples in a vector of length ns. The values ??of every single element of the vector will be given by the following equation:
S[i] = (sin(i*(2p/ns))+1)
We know that the sinusoidal signal varies between 1 and -1 so it is necessary to shift it upwards to have a positive sinewave (therefore not with a null average value) therefore it must be moved to the middle of the reference voltage. To do this, it is necessary to retouch the previous equation with the following:
S[i] = (sin(i*(2p/ns))+1)*((0xFFF+1)/2)
Where 0xFFF is the maximum digital value of DAC (12bit) when the data format is 12 bits right aligned.
To set the frequency of the signal to be generated, it is necessary to handle the frequency of the timer trigger output (freq.TimerTRGO, in our case we use the TIM6) and the number of samples.
Fsinewave = freq.TimerTRGO/ns
In our case, we define Max_Sample = 1000 ( is uint32_t variable) and let's redefine some values of the timer 6 initialization.
static void MX_TIM6_Init(void) { /* USER CODE BEGIN TIM6_Init 0 */ /* USER CODE END TIM6_Init 0 */ TIM_MasterConfigTypeDef sMasterConfig = {0}; /* USER CODE BEGIN TIM6_Init 1 */ /* USER CODE END TIM6_Init 1 */ htim6.Instance = TIM6; htim6.Init.Prescaler = 1; htim6.Init.CounterMode = TIM_COUNTERMODE_UP; htim6.Init.Period = 100; htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim6) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM6_Init 2 */ /* USER CODE END TIM6_Init 2 */ }
We have changed the following parameters:
htim6.Init.Prescaler = 1; htim6.Init.Period = 100;So with 1000 samples, the output sinewave will be a frequency of 10 Hz. We can change the number of samples (being careful not to use too few samples) or the “Init.Prescaler” and “Init.Period” values of timer 6.
Using the DAC in DMA mode the HAL library makes available to handle the DAC another function to set the DAC output.
HAL_DAC_Start_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t* pData, uint32_t Length, uint32_t Alignment)
Compared to the manual function we find two more parameters:
As you can see from the following code we first need to include the "math.h" library, define the value of pigreco (pi 3.14155926), and write a function to save the sampled sinewave values in a array (we wrote a function declared as get_sineval () ).
#include "math.h" #define pi 3.14155926 DAC_HandleTypeDef hdac; DMA_HandleTypeDef hdma_dac_ch1; TIM_HandleTypeDef htim6; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_DAC_Init(void); static void MX_TIM6_Init(void); uint32_t MAX_SAMPLES =1000; uint32_t sine_val[1000]; void get_sineval() { for (int i =0; i< MAX_SAMPLES; i++) { sine_val[i] = ((sin(i*2*pi/MAX_SAMPLES)+1))*4096/2; } } /* USER CODE END 0 */
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_DAC_Init(); MX_TIM6_Init(); /* USER CODE BEGIN 2 */ get_sineval(); HAL_TIM_Base_Start(&htim6); HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, sine_val, MAX_SAMPLES, DAC_ALIGN_12B_R); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
Now we just have to show the acquisition that the oscilloscope:
If we change the number of MAX_SAMPLE to 100 the sinewave will be a frequency of 100 Hz, but as can be seen from the acquisition, the number of samples, in this case, is a bit low.
We can optimize the waveform by acting on timer 6 in order to increase the number of samples. For example by modifying htim6.Init.Period = 400
An Analog to Digital Converter (ADC) converts a continuous signal (usually a voltage) into a series of discrete values ??(sequences of bits). The main features are:
There are different types of ADCs, the most common are listed below (illustrating their operation is not the purpose of this article):
Generally, STM32 microcontrollers have at least one ADC (a SAR ADC) with the following characteristics:
Fc = ADCCLK / (12.5 + Number of cycles)
Each ADC has two conversion modes: “regular” and “injected”.
Furthermore, the "Dual Conversion Modes" can be active:
In the STM32 with almost two ADCs and it is, therefore, possible to perform different conversion modes: in these types of conversion the ADC2 acts as a slave while the ADC1 acts as a master allowing 8 different types of conversion.
We are now ready to write a first simple example using the ADC peripheral. The goal is to measure the voltage in a voltage divider composed of a fixed value resistor and a potentiometer (so that by moving the potentiometer cursor, the voltage to be read varies) we begin by configuring our peripheral with STCube Tool. For this project, we will use the NUCLEO STM32L053R8. This board has only one ADC with 16 channels and a resolution of up to 12bit.
Now we’ll see the configuration step by step:
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | STM32 Nucleo | Amazon | Buy Now |
We have to flag IN0 to activate Channel 0, then we can configure the peripheral. Channel 0 is on GPIO PA0 as we can see in the picture below:
We select the ADC_prescaler equal to 4, resolution to 12bit (maximum of a resolution, we can choice between 6, 8, 10 and 12 bits), “right data alignment” (we can choose between right and left alignment), and “forward” as scan direction (we can choose between forward and backward).
For this first example we’ll hold disabled Continuous, Discontinuous conversion and DMA mode. Furthermore, the ADC sets, at the end of single conversion, the EoC (End of Conversion) flag.
We select 12.5 Cycles as sampling time (in this way the sampling frequency is 320 kHz obtained from the formula described above), the start of conversion is triggered by software. Furthermore, for this application the watchdog is disabled.
After the generation of the initialization code with STCube, we can find in our project the ADC configuration. As for every peripheral, the HAL library defines the dedicated C structure, for the ADC defines “ADC_HandleTypeDef”.
In our case the “ADC1” is the instance that points to our ADC. The structure “ADC_InitTypeDef” is used to handle the configuration parameters. In our example is generated as follow:
static void MX_ADC_Init(void) { /* USER CODE BEGIN ADC_Init 0 */ /* USER CODE END ADC_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC_Init 1 */ /* USER CODE END ADC_Init 1 */ /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) */ hadc.Instance = ADC1; hadc.Init.OversamplingMode = DISABLE; hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; hadc.Init.Resolution = ADC_RESOLUTION_12B; hadc.Init.SamplingTime = ADC_SAMPLETIME_12CYCLES_5; hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD; hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc.Init.ContinuousConvMode = DISABLE; hadc.Init.DiscontinuousConvMode = DISABLE; hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc.Init.DMAContinuousRequests = DISABLE; hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV; hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED; hadc.Init.LowPowerAutoWait = DISABLE; hadc.Init.LowPowerFrequencyMode = ENABLE; hadc.Init.LowPowerAutoPowerOff = DISABLE; if (HAL_ADC_Init(&hadc) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel to be converted. */ sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = ADC_RANK_CHANNEL_NUMBER; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN ADC_Init 2 */ /* USER CODE END ADC_Init 2 */ }The function HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) needs to initialize the peripheral and define the clock and the GPIO ( in our case PA0).
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(hadc->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspInit 0 */ /* USER CODE END ADC1_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**ADC GPIO Configuration PA0 ------> ADC_IN0 */ GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ } } /** * @brief ADC MSP De-Initialization * This function freeze the hardware resources used in this example * @param hadc: ADC handle pointer * @retval None */The function HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc) needs to de-initialize the peripheral.
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc) { if(hadc->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspDeInit 0 */ /* USER CODE END ADC1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_ADC1_CLK_DISABLE(); /**ADC GPIO Configuration PA0 ------> ADC_IN0 */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0); /* USER CODE BEGIN ADC1_MspDeInit 1 */ /* USER CODE END ADC1_MspDeInit 1 */ } }
Before describing the code let's see how to make the connections on the development board.
We need a 10kOhm potentiometer and a 2kOhm resistor. The potentiometer is connected between 3.3V and 2kOhm resistor, the common point is connected to PA0, and finally, the other end of the 2k Ohm resistor is connected to the ground pin.
Acting on the potentiometer we will see the read voltage vary from 3.3 Volt to about 0 Volt.
Now let's dive into the code: In the Includes section we add the header file of main./* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */
In “Private variables” section we find “ADC_HandleTypeDef hadc” as previous said is an instance to C structure to handle the ADC peripheral. Then, we add three variables:
/* Private variables -----------------------*/ ADC_HandleTypeDef hadc; /* USER CODE BEGIN PV */ const int Resolution = 4095; const float Vs =3.300; float volt; /* USER CODE END PV */
Then, we can find the protype of function to handle the peripherals and resources initialized (system timer, GPIO, and ADC).
/* Private function prototypes -----------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_ADC_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */
Finally, the main starts.
In the first part we call functions to initialize the peripherals and resources used:
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_ADC_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */
In the second part, that is, inside an infinite loop (while (1)) there is the function to start the conversion of the ADC, read the data and save it in the variable volt and finally stop the conversion wait for a second and start with the conversion and so on.
while (1) { /* USER CODE END WHILE */ HAL_ADC_Start(&hadc); if(HAL_ADC_PollForConversion(&hadc,10)==HAL_OK) { volt=HAL_ADC_GetValue(&hadc)*Vs/Resolution; } HAL_Delay(1000); HAL_ADC_Stop(&hadc); /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
Now, once our code has been compiled, we can debug it in real-time, just press the "spider" icon (see figure below) and see how the volt variable varies by acting on the potentiometer.
Once we have clicked on the debug button, at the top right, we can select the "live expression" window and add (by writing the name in the table) the variable to be monitored.
Now we can start the debug by clicking on the “Resume” button (on the top right) or by pressing the F8 key (on our keyboard).
We are now ready to read our voltage value. We will see that by acting on the potentiometer we will read the voltages in the whole range considered.
Some measures are shown below:
The SPI (Serial Peripheral Interface) protocol, or rather the SPI interface, was originally devised by Motorola (now Freescale) to support their microprocessors and microcontrollers. Unlike the I2C standard designed by Philips, the SPI interface has never been standardized; nevertheless, it has become a de-facto standard. National Semiconductor has developed a variant of the SPI under the name Microwire bus. The lack of official rules has led to the addition of many features and options that must be appropriately selected and set in order to allow proper communication between the various interconnected devices. The SPI interface describes a single Master single Slave communication and is of the synchronous and full-duplex type. The clock is transmitted with a dedicated line (not necessarily synchronous transmission that has a dedicated line for the clock) and it is possible to both transmit and receive data simultaneously. The figure below shows a basic connection diagram between two peripherals that make use of the SPI interface.
From the figure, it is immediately possible to notice what has just been said, namely that the communication generally takes place between a Master and a Slave. The interface presents 4 connection lines (excluding the ground however necessary), for which the standard SPI is also known as 4 Wire Interface. The Master starts the communication and provides the clock to the Slave. The nomenclature of the various lines in the SPI interface is normally as follows:
In addition to this standard nomenclature, there are other acronyms.
For example:The first advantage in SPI communication is faster communication, instead, the first disadvantage is the presence of the SS pin necessary to select the slave. It limits the number of slave devices to be connected and considerably increases the number of lines of the master dedicated to SPI communication as the connected slaves increase.
To overcome these problems, the devices in the daisy chain can be connected (output of a device connected to the input of the next device in the chain) as shown in the figure below where a single slave selection line is used.
The disadvantages, however, are the lower updating speed of the individual slaves and signal interruption due to the failure of an element.
We can use this communication to put in communication our micro-controller with different peripherals as Analog-Digital Converters (ADCs), Digital-Analog Converters (DACs), EEPROM memories, sensors, LCD screen, RF module, Real Time Clock, etc.
The STM32 micro-controllers provide up to 6 SPI interfaces based on the type of package that can be quickly configured with STCube Tool.
STCube Tool initializes the peripherals with HAL (Hardware Abstraction Layer) library. The HAL library creates for SPI (as all peripherals) an C structure:
As just said to initialize the SPI peripheral to be used, it is necessary to use the struct SPI_InitTypeDef. It is defined as follow:
When we use the STcubeMX to initialize the SPI peripheral we are modifying this structure
In details:If the slave supports, the full-duplex communication can be enabled.
By enabling the SPI and the chip select pin, the pins available on the microcontroller are automatically chosen to manage this interface (but they can be changed by looking for the alternative functions of the different pins of the microcontroller). For example, in our case the following pins are selected:
Now you can generate the initialization code. Before being able to write the first code to manage this communication interface, it is necessary to understand the functions that the libraries provide and the different communication modes.
As for other communication interfaces, the HAL library provides three modes to communicate: polling mode, interrupt mode, and DMA mode.
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | STM32 Nucleo | Amazon | Buy Now |
Using the SPI in Polling Mode is the easiest way, but it is the least efficient way as the CPU will remain in a waiting state for a long time. HAL library provides the following functions to transmit and receive in polling mode:
Master receives data packets in blocking mode (polling mode).
The parameters are:
Master transmits data packets in blocking mode (polling mode).
If the slave device supports, the full-duplex mode:
Master transmits and receives data packets in blocking mode (polling mode).
The parameters are:
Using the SPI in Interrupt Mode, also called non-blocking mode. In this way, the communication can be made more effective by enabling the interrupts of the SPI in order to receive, for example, signals when the data has been sent or received. This improves CPU time management. In applications where all the management must be deterministic and it is not known when an interrupt can arrive, these can potentially manage the time management of the CPU, especially when working with very fast buses such as SPI. We can enable the SPI interrupts directly during the initialization with STCube Mx.
HAL library provides the following functions to transmit and receive in interrupt mode:
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef * hspi) { // Message received .. Do Something ... }
Master transmits data packets in blocking mode (interrupt mode).
To handle the interrupt needs to write our code in the callback:
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef * hspi) { // Message transmitted.... Do Something ... }If the slave device supports, the full-duplex mode:
Master transmits and receives data packets in non-blocking mode (interrupt mode).
To handle the interrupt needs to write our code in the callback:
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef * hspi) { // Message transmitted or received.. .. Do Something ... }
Using the SPI in DMA Mode the SPI bus can be used at its maximum speed, in fact, since the SPI must store the received and transmitted data in the buffer to avoid overloading it is necessary to implement the DMA. In addition, use by DMA mode frees the CPU from performing "device-to-memory" data transfers. We can easily configure the DMA during the initialization using STCubeMx :
In this case, the DMA is enabled in normal (we can use it in circular mode) mode both in transmission and reception
HAL library provides the following functions to transmit and receive in DMA mode:
Master receives data packets in non-blocking mode (DMA mode).
The SPI device receives all bytes of data in the buffer one by one until the end in DMA mode. At this point, the callback function will be called and executed where something can be done.
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef * hspi) { // Message received .. Do Something ... }
Master transmits data packets in non-blocking mode (DMA mode).
The SPI device sends all bytes of data in the buffer one by one until the end in DMA mode. At this point, the callback function will be called and executed where something can be done.
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef * hspi) { // Message transmitted….. Do Something ... }
If the slave device supports, the full-duplex mode:
Master transmits and receives data packets in non-blocking mode (DMA mode).
The SPI device sends or receives all bytes of data in the buffer one by one until the end in DMA mode. At this point, the callback function will be called and executed where something can be done.
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef * hspi) { // Message transmitted or received.... Do Something ... }
We are now ready to handle an SPI communication with STM32.
EEPROMs (Electrically Erasable Programmable Read-Only Memories) allow the non-volatile storage of application data or the storage of small amounts of data in the event of a power failure. Using external memories that allow you to add storage capacity for all those applications that require data recording. We can choose many types of memories depending on the type of interface and their capacity.
EEPROMs are generally classified and identified based on the type of serial bus they use. The first two digits of the code identify the serial bus used:
Now we will see how to write or read data on an I2C EEPROM like 24C256C. This serial EEPROM is organized as 32,768 words of 8 bits each. The device’s cascading feature allows up to eight devices to share a common 2-wire bus. It is available in various 8-pin packages
The device can be used in applications consuming low power. The device is available in all standard 8-pin packages. The operating voltage is comprised of between 1.7V and 5.5V.
In our example, we connect A0, A1, A2 directly to VCC in this way the device address is 1010111 (in general A0, A1, A2 identify the last three significant bits of the device address 1 0 1 0 A2 A1 A0) is 0x57 in Hexadecimal. The 4 most significant bits are preset (Control Code), the A0, A1, A2 are Chip Select Bits.
Now we start with our project using STNucleoL053R8 and STCube to generate the initialization code. Below is shown the connection
So, we configure the I2C1 using STCube and leave all configuration as it is and we will operate in polling mode.
In GPIO setting select PA9 (SDA) and PA8 (SCL).
Our project has been initialized by STCubeMX. In the /Core/Src/main.c we will find our main where we will write the main body of our program.
Now let’s see what the code generator did:
First of all, we find the “Include” section we can add the library needed.
/* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h"In our case we can add also “stm32l0xx_hal.h” library to be able to use HAL library (I2C HAL library included)
#include "stm32l0xx_hal.h " #include "Var.h " #include "Funct.h "In “Private variables” has been defined two privates variable htim2 and hi2c1;
/* Private variables ---------------------------------------------------------*/ TIM_HandleTypeDef htim2; UART_HandleTypeDef hi2c1; unsigned short int address; // eeprom address unsigned char EEP_pag = 0x00 // EEPROM page unsigned char EEP_pos = 0x00 // EEPROM position unsigned char rdata = 0x00 // to store the data read from EEPROMIn “Private function prototypes” we find the protype of function to initialize the System Clock, GPIO, timer and peripheral:
/* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM2_Init(void); static void MX_I2C1_Init(void);
This function has been generated automatically by STCubeMx with the parameter selected.
The main contains the initialization of the peripherals and variables, before the while loop the code call the function Write_EEPROM() and Read_EEPROM() to write and read a data in a specific address of the EEPROM. these functions were written in EEPROM.c, a C file added to our project in the src folder.
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM2_Init(); MX_I2C1_Init(); /* USER CODE BEGIN 2 */ address = 0x00 << 8 | 0x00 // eeprom page 0 , position 0 // Now we want to store 10 in page 0x00 and position 0x00 of EEPROM Write_EEPROM(address, 10, 0) // Now we want store in rdata variable the content of cell memory 0x0000 rdata = Read_EEPROM(address, 0) /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }Furthermore, we have added two header files and one c file:
/** Var.h * Created on: 27 ott 2021 * Author: utente */ #ifndef INC_VAR_H_ #define INC_VAR_H_ extern unsigned char buf[20]; extern int i; #endif /* INC_VAR_H_ */
/** Funct.h * Created on: 28 ott 2021 * Author: utente */ #ifndef INC_FUNCT_H_ #define INC_FUNCT_H_ extern unsigned char Read_EEPROM(unsigned int, unsigned char); extern void Write_EEPROM(unsigned int, unsigned char, unsigned char); #endif /* INC_FUNCT_H_ */
/** Serial.c * Created on: Oct 29, 2021 * Author: utente */ #include "stm32l0xx.h" // Device header #include "stm32l0xx_hal_conf.h" #include "stm32l0xx_hal.h " #include "Var.h " #include "Funct.h " extern I2C_HandleTypeDef hi2c1; unsigned char Read_EEPROM(unsigned int addr, unsigned char device) { unsigned char page; uint8_t dato; page=0xAF; // due to chip select bits setting HAL_I2C_Mem_Read(&hi2c1,page, addr, I2C_MEMADD_SIZE_16BIT, &dato,1,5); return dato; } void Write_EEPROM(unsigned int addr, unsigned char dato, unsigned char device) { unsigned char page; page=0xAF; //due to chip select bits setting HAL_I2C_Mem_Write(&hi2c1,page, addr, I2C_MEMADD_SIZE_16BIT, &dato,1,5 ); while(HAL_I2C_IsDeviceReady(&hi2c1, 0xA0, 1, HAL_MAX_DELAY) != HAL_OK); HAL_Delay(10); }Now we are ready to compile and run the project:
Hello friends, I hope you all are doing great. In today's lecture, we will have a look at the I2C Communication with STM32 Microcontroller board. I am going to use the Nucleo board for today's lecture. In the previous lecture, we have discussed STM32 Serial communication both in Interrupt Mode and polling Mode. Today, we will study another way of communication(i.e. I2C) with STM32. So, let's first have a look at what is I2C Communication:
Where To Buy? | ||||
---|---|---|---|---|
No. | Components | Distributor | Link To Buy | |
1 | STM32 Nucleo | Amazon | Buy Now |
I²C (Inter-Integrated Circuit) is a two-wire serial communication system used between integrated circuits. Like any serial protocol, one of its advantages is that of using only two lines that transmit or receive a sequence of bits, the limit is the communication speed which has been improved over the years.
The bus was conceived and developed by Philips (now NXP) It was designed to overcome the difficulties inherent in the use of parallel buses for communication between a control unit and various peripherals.
Serial transmission is a mode of communication between digital devices in which bits are sent one at a time and sequentially to the receiver in the same order in which they were transmitted by the sender. Although the communication modules are more complex than the parallel transmission, the serial mode is one of the most widespread especially in communications between chips that must communicate with each other over great distances, because:
SDA and SCL lines need to be pulled up with resistors. The value of these resistors depends on the bus length ( ie the bus capacitance) and the transmission speed. The common value is 4.7kO. In any case, there are many guides to size them and we refer their reading to the more attentive reader.
The transmission mode is Half-duplex ie the transmission between devices is alternated.
As shown by the previous image, we can use this communication to put in communication different peripherals as Analog-Digital Converters (ADCs), Digital-Analog Converters (DACs), EEPROM memories, sensors, LCD screen, RF module, Real-Time Clock, etc.
The Nucleo boards provide one or more I2C interfaces that can be quickly configured with STCube Tool.
There are four modes of operation:
The first two are used to operate in slave mode, while the last two are in master mode. By default, the interface is configurated in slave mode.
By default, the I2C interface operates in Slave mode, but it is possible to switch to Master mode to send a Start condition message. Furthermore, it needs to write in I2C_CR2 register the correct clock configuration to generate the expected timings. The Master sends a Stop condition when the last data byte is transferred, and the interface generates an interrupt.
In general, the packet message is as follow:
Furthermore, there are three ways to exchange data, named:
HAL library provides the following functions to transmit and receive in polling mode:
HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)The parameters are:
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)The additional parameters are:
HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)Master receives in master mode an amount of data in non-blocking mode with interrupt.
HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)Master transmits in master mode an amount of data in non-blocking mode with interrupt.
HAL_I2C_Slave_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)Slave receives in master mode an amount of data in non-blocking mode with interrupt.
HAL_I2C_Slave_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)Slave transmits in master mode an amount of data in non-blocking mode with interrupt.
HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)Master receives in master mode an amount of data in non-blocking mode with DMA.
HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)Master transmits in master mode an amount of data in non-blocking mode with DMA.
HAL_I2C_Slave_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)Slave receives in master mode an amount of data in non-blocking mode with DMA.
HAL_I2C_Slave_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)Slave transmits in master mode an amount of data in non-blocking mode with DMA. In the STCube tool, the I2C can be configurated fastly and easily as follow.
In Pinout & Configuration, widow selects Connectivity and selects one of the available I2C (I2C1, I2C2, etc). In parameter settings, the master and slave features can be set. Master features are I2C speed mode (standard mode by default and fast mode) and the I2C clock speed (Hz). In standard mode, the device can send up to 400kbit/s while in fast mode up to 1Mbit/s. In general, like clock speed, the STM32 supports 100kHz, 400kHz and sometimes 1MHz.
The main feature of slaves is the primary address length that in general, as previously said, is 7-Bit. Furthermore, the slave can have a secondary address.
Then need to configure the GPIO, as follow:
Now the I2C configuration is terminated and can be possible to generate the code initialization and finally be ready to write our application.