📜 ⬆️ ⬇️

Software Development for DSP TMS320F28 motor control

In my first article, I spoke about this family of controllers, I wrote in a dozens of people with questions about him, although this was not the topic of the article. People categorically did not want to go to Google, speaking about the lack of information. I was a little surprised and decided to check - in fact there is practically nothing in the Russian language for the C2000 family (against the background of AVR, STM), and most importantly there are no understandable starting guides. Information can be found in English, but again it is not enough. For me, this is somewhat surprising, given that this family of years is not enough. Therefore, it was decided to the best of my ability to influence the situation.

Who needs these controllers in principle ... Do you want to assemble a welding inverter for yourself? Uninterrupted power supply unit? Rectifier for galvanic bath? Chastotnik? Inverter for alternative energy? CNC machine? If at least one item is about you, then the article is dedicated to you!

Other readers will also be interested to know about the "new-old" controller, why it is needed and how to work with it. This family is very simple (much simpler than STM, LPC and other Cortexes), it is easy to buy stones (Ali also exists), they make it possible to implement highly reliable industrial solutions, you can build almost any industrial control system on their basis.

You have already decided that this controller is your dream and are ready to rush into battle? Then buy for $ 17 just such a debugging of the F28027-LaunchPad:


Bought? Now you can and fight. If the question arose where to buy "better" and "cheaper", then go to the official store. Go here and see the price tag of $ 17. For this amount you will receive the original debug board and delivery by courier to the door. I ordered once in China for delivery, it was $ 16 and it’s with a discount and a coupon, as well as a “bonus” trip to the post office. Therefore, I recommend it official. Go!

C2000 Series Review


You can read more about everything on the official website , of course in English. I will briefly talk about it and express my thoughts about the application. Please note that this is only my conjecture and they do not pretend to the truth.

First, a few words about the C2000 in general. The distinctive features of the family, which are related to its main purpose of motor control, is the presence of HRPWM (high-precision PWM) and CLA (coprocessor). The last one is absent in the youngest Piccolo TMS320F2802x, but it is not needed there, the main thing is that HRPWM is in place. What is it ... Actually HRPWM is a normal PWM, only very accurate and the time of recording and installation of the new fill factor is noticeably faster. This allows you to get, for example, a different sine shape in a DC / AC inverter or with very high accuracy to control stepper motors in a CNC machine.

CLA is essentially a full-fledged core, only it does not have access to the periphery, only to the main core and memory. It serves to unload the main core from the calculations. Very easily and naturally, this coprocessor digests the data float, which is important when implementing various algorithms, filters and other things.



I will talk about the two main families that you will likely encounter:


Debug card upgrade


When you get a box with a debug board, you will find the missing quartz and capacitors to it. They are not necessary, but it is desirable to solder. Kvartsev in the case of the HC-49 I have not found, so I had to borrow from a friend. He picks up the AVR and STM, so he only found 8 MHz. Sealed. 22 pF capacitors added according to the old memory, it seemed to do so on megas at school.

This solution is not the best and is connected with the PLL setting. The maximum multiplier for the PLL is x12 (it can be more, but not recommended and it works crookedly). The maximum frequency is 60 MHz. The maximum value of the divider at the PLL output is 3. The frequency of the quartz is 8 MHz. I can not multiply by 8 by an integer and get 60. We are looking for the closest overall value, in this case it is 240. That is, multiplying 8 by 30, and then dividing by 4 I will get the cherished 60 MHz, but then the trouble is x30 PLL multiplier is not allowed , divider / 4 is also unavailable. There are 2 ways out:


Architecture and Peripheral Features


Everything that will happen next refers to the controller TMS320F28027. To begin, let's just look at the structure that is taken from the datasheet :



The first thing you should pay attention to is that RAM is divided into 3 sectors: M0, M1 and SARAM. The volumes of these memory sectors in our case are 1k, 1k and 4k. It is worth noting that in this case the word is not 8 bit, but 16 bit, that is, in a more familiar form, this is 2 kByte + 2 kByte + 8 kByte. The peculiarity of this module is that the M0 and M1 sectors are more nimble than the remaining 8 KB of RAM. Formally, you can take it as a cache. In sectors M0 and M1 usually store the most frequently used data, as well as data that is most critical to memory performance. By default, we can specify with the help of the linker what is stored where, but I will not raise this topic in this article, here a separate article is needed as a minimum.

The second important feature is that all the peripherals are clocked from the system bus, that is, from 60 MHz (in my case 56 MHz). As an example, I will give the STM32 microcontrollers, where the core frequency is, for example, 180 MHz for F4, but the periphery is clocked through a number of dividers. For myself, I call this approach “fake megahertz,” although this is quite exaggerated. Therefore, 60 MHz for TMS320F28 and 180 MHz for stm32 are not that different, and if we recall the presence of CLA, then 60 + 60 MHz is at least comparable. It is clear that such a comparison is not correct, but it clearly makes it clear that not only megahertz are full.

Just an interesting point - pay attention to the general structure: HRPWM, ADC, comparators with an internal DAC, encoder processing module (eCAP) ... Ready-made frequency converter with vector control in its pure form! This is the essence of this family - minimalism. On the one hand, the periphery is rather poor against the background of Cortex, but on the other hand it is sufficient to implement both the frequency counter, dc / dc, and dc / ac, and the driver of the stepping motor. Due to this, working with TMS320F28 controllers is very simple, understandable and not overloaded with unnecessary actions. But if you suddenly need 3 UARTs, and for them a pair of i2c and 3 more SPIs, then these controllers are definitely not for you - they have different tasks.

Environment and Development Environment



Seeing the splash logo? Remember her. If you decide to start using in the development of the article’s heroes, then this software is yours, and as you can see, as you can see, controlSUITE .

This application is a collection and library of all that is needed for software development for the C2000 family of controllers. Installing this application is the first thing to do and see its composition, it contains:


All that I have described above is very brief and modest. It will take a couple of weeks for you to browse and scroll through everything at least diagonally. Of course, most of this amount of data at the start you do not need, but you need to remember where to go, if something is not clear to you and wonder.

Now what we will work with today is the IDE, the graphic part of which is based on the well-known Eclipse. The compiler is not GCC, but its own from Texas, which in my subjective opinion is definitely better than the first. Although there are suspicions that it is thoroughly doped all the same gcc. The development environment is called Code Composer Studio , the current version 7.4.

Project creation


At first, I wanted to implement a task identical to the first article, that is, to draw a sine. In principle, within the framework of a single article, this could be done, leaving a very large amount of trifles behind the scenes, but as you know, the essence is just in trifles. There are several articles on TMS on the Internet, but all of them are very superficial and boil down to the form “we copy it and it all works”, that is, the process and ideology are not considered at all. Therefore, within the framework of this article we will create a project, clean it of unnecessary components, set up the firmware in the controller's flash memory and learn how to work with the GPIO, and they are very interesting here.

Download CCS7 from the manufacturer’s website, install and proceed to create the project in the usual way: File → New → CCS Project ...


We see this window and in it we need to select the controller we are interested in, in my case it is TMS320F28027, specify the name of the project and set the path where it will be stored. First you need to create a folder where the project will be stored. The project name and the folder name may not match. Click the Finish button and our project is created.

Now you need to fill our project content and connect it. Before this, to improve the structure of the project, we create a set of folders like this:



Please note that this structure is not some kind of dogma, but is only my idea of ​​orderliness. Although I still recommend it to people with minimal experience, over time you will change it to fit your worldview, but for now it minimizes the number of jambs.

Now we create a main.h file in the inc folder and connect it to main.c. Through it there will be connections of base libraries. And before starting to transfer libraries and other files, let's set the paths to the folders where our future header files will be stored in the project settings. To do this, in the project tree, right-click on the project (Test) and at the very bottom click Properties or simply press Alt + Enter . In the opened window, go to Build → C2000 Compiler → Include Options and here we need to go to the two existing paths - set the path to the inc and system_inc folders. Click Add , then Workspace and then to the desired folder inc, then do the same and cling to the second folder. Thus, we prescribed relative paths, and we don’t have to reconfigure anything during project transfer. As a result, we get this picture and click Ok :



Now we have an empty project with prescribed paths and other settings, it remains only to fill it with libraries. The only thing that needs to be done is to check whether everything is connected. In theory, you should have such a code and a picture, compile the project. To do this, press CTRL + B or at the top go to Project → Build All . The project should compile without errors and look like this (the image is clickable):



Now let's talk a little about linker. Initially, when creating an IDE project, it generates the file 28027_RAM_lnk.cmd , it places our program in RAM in the process of debugging and flashing. This is useful when we are debugging, because flash memory resources are not wasted, and debugging in RAM is significantly faster. But what if we want to sew into a flash? For this there is another link file, which will place our program in the flash. I will show this option.

First we delete the file 28027_RAM_lnk.cmd . As I said before - our controlSUITE is everything. Open it. Now we go English → Device → Piccolo F2802x → Supporting Libraries → Header Files for F28027x . On the right we see the folders - these are the standard libraries and everything you need, including linkers. Now we go to the folder f2802x_common → cmd and here we see a set of linkers for all the stones of the ruler. As it is not difficult to guess the _RAM files for filling the code into the RAM, and without this tag for filling the flash. Take the file F28027.cmd and copy to our project instead of the old remote linker.

Now it's time to migrate the libraries themselves. Go to the folder f2802x_common → source and see a bunch of files. There are two types of libraries: standard registers (analogous to CMSIS) and some kind of SPL. In this case, we are only interested in the first view, that is, files with the prefix f2802x_ . Of course, you can drag all of them into our project, but why litter it if we don't use everything? If you need something, then in the future just add. For now, we restrict ourselves to the following filesets:


Copy the data file and paste it into our system_src folder. Now go to the folder f2802x_headers → source and pick up the file F2802x_GlobalVariableDefs.c from there and copy it again to our system_src folder. Next, go to the folder f2802x_headers → cmd and copy the file F2802x_Headers_nonBIOS.cmd from there to the same folder. This is where the filling of the system_src folder is complete and go to the headers.

Go to the folder f2802x_headers → include and copy all the files from there to our system_inc folder. Now go to the folder f2802x_common → source and copy the files from there:


We should have this picture in the project tree:


Now you need to connect the base libraries, the file main.h takes the following form:

#pragma once #include "F2802x_Device.h" #include "F2802x_examples.h" 

We try to compile. If the project was compiled without errors and Warnings, then everything is properly connected. If this does not happen, then double-check everything 10 times, and if it doesn’t work out at all, then write to ps - I will help, as Owl said: “Bez-voz-mez-bottom, that is for nothing” .

Starting initialization of the controller and clocking system


In this section, we will write a function that initializes the watchdog timer and interrupt vector, will reset interrupt flags. We also configure the clocking system, as a result of which the external quartz, rather than the internal RC-chain, will become the clocking source, configure the PLL and enable the clocking for all peripherals.

For code accuracy, I propose to render all the basic initializations into a separate file, whose front-end will be the void InitStartSystem (void) function. To do this, create the files systemInitStart.h and systemInitStart.c . I'll write the function right away and then just sort its contents:

 void InitStartSystem (void) { DisableDog(); XtalOscSel(); InitPll(TMS320_PLLCR, TMS320_DIVSEL); InitPeripheralClocks(); InitPieCtrl(); InitPieVectTable(); } 

All functions that are called in InitStartSystem () are standard. I advise you to see in detail how they are implemented, for this you can hold down the CTRL and click on the function of interest. Have you looked? Now let's take a quick run ...


Actually here it is all initialization. I think many have noticed the "important points" associated with the function of Delay . Why did we cut it on the vine? Yes, everything is simple - it's a crutch.

TI engineers have added these completely unnecessary delays to some of the functions, added in recent updates. Why - a mystery not only for me. Registers and other critical entries and so protected, so it will not make our controller stupid. By the way, when you initialize in power electronics, you can not "blunt" at all, otherwise they will be women. Therefore, forget forever the delay functions and other devils, only timers! Delays are permissible only for some training purposes, for example, to flash quickly with a LED.

In order to make sure that the code works , we call the initialization function in main, compile, flash and cling to the oscilloscope on the GPIO18 . This output is similar to the MCO of the STM32, that is, it outputs the system frequency. The oscilloscope should see a signal with a frequency of 56 MHz. If the oscilloscope is good, then you will see a meander, if the Chinese (even good), then most likely it will be something closer to the sine. Setting GPIO18 to display the system frequency can be seen in the InitPeripheralClocks () function. First you need to "connect" gpio to the output frequency, and then set the divider equal to 1:

  GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 3; // GPIO18 = XCLKOUT SysCtrlRegs.XCLK.bit.XCLKOUTDIV=2; // Set XCLKOUT = SYSCLKOUT/1 

GPIO setup


To work with this family, we need only a reference manual, which the developers at TI have divided into several files, each of which describes a certain periphery, which is very convenient. Download the datasheet here and go to the Documentation Support section on page 126. Here we see a set of links to documentation with its brief description: errata, a guide for starting engine management and guides on each periphery. We are interested in a document called TMS320F2802x / TMS320F2802xx Piccolo System Control and Interrupts Reference Guide , which contains the description of the GPIO and other basic system settings that we are interested in. We look at the block diagram of GPIO:



We see a fairly familiar picture of the device I / O ports. Here and the ability to enable internal suspenders, and the use of a signal with GPIO for interrupts and other amenities. The main feature of this periphery in the C2000 is the possibility of hardware interference suppression, for example, the bounce of mechanical button contacts. Let's look at an interesting diagram:



It shows the principle of reading the status of inputs. In most controllers, the input state is read at the clocking frequency of this peripheral, that is, by default, with a frequency of 56 MHz in our case, and for the same stm in the older families, these frequencies are even higher. I think everyone understands that at this frequency the controller has time to "see" any interference and noise. Sometimes this frequency is needed, and sometimes not, for example, if we need to poll a button. Why do we need to poll her every 18 ns? Therefore, we implemented the possibility of decreasing the clocking frequency of a specific port using the CTRL register and QUALPRDx bits, where X takes a value from 0 to 3: QUALPRD0 is responsible for GPIO0 ... 7, QUALPRD1 is responsible for GPIO8 ... 15 and so on. In fact, this is a common frequency divider with a ratio from 1 to 510.



It often makes no sense to poll the button, so we will tune the divider to 510, that is, to the maximum. We look again at the diagram and see that the signal is considered steady only when its level was unchanged at 6 cycles. The number of cycles required for fixing can be 1, 3, or 6. The larger the divider and the more cycles we fix, the more stable the bounce protection operation will be. When there will be a bounce of contacts, then at first it will be chaotic transitions from 0 to 1 and back, when the bounce will pass and the signal will stop and will not change for 6 cycles, this will mean that the button is pressed. All ingenious is simple.

Now let's consider the main registers, we will not affect interruptions - only the settings of the ports themselves. First you need to say that the registers are divided into 2 types: configuration registers and data registers . The former are responsible for the configuration of properties, for example, input or output. The second group is responsible for recording and reading the state of the port.

Configuration registers:


Data registers:


Here is such a simple set of registers. Even from the description above, it is already possible to understand what needs to be done to configure the port, but prudent engineers or technical writers from TI have made step-by-step instructions:



I will say right away that steps 6 and 7 are not needed for us, because Neither the dog nor the interruption, we do not use in this article. I will describe the rest of the steps briefly for people who learned German at school:


That's the whole setup, as you can see it is elementary and logically understandable. At once I want to note that it is not necessary in this order to make adjustments, for example, you can adjust the direction (input or output) in the very first step. It does not matter.

Super important!

When working with registers in the C2000 family, it is necessary to take into account the moment that they are protected. Everything described further concerns mainly the registers of the setting group. If you carefully watched the standard functions, you probably saw some strange commands there: EALLOW; and EDIS; . EALLOW command - removes protection and opens access to work with system registers. The EDIS command enables back protection and opens access to work with system registers. That is, any work with system registers should ALWAYS look like this:

 EALLOW; // Работаем с системными регистрами, меняем их значения, настраиваем EDIS; 

Such an operation is not required if we work with data registers, for example, if we set our output to “1” with the help of the GPxSET register, then we don’t have to remove protection from it and switch it back on accordingly. The documentation says everywhere that you need to protect and what does not, for example, like this:



Based on all the above, let's configure GPIO0 ... 3 with LEDs on the output. I propose to put all the GPIO settings into the InitLEDgpio function and write it:

 void InitLEDgpio (void) { EALLOW; GpioCtrlRegs.GPADIR.bit.GPIO0 = 1; GpioCtrlRegs.GPADIR.bit.GPIO1 = 1; GpioCtrlRegs.GPADIR.bit.GPIO2 = 1; GpioCtrlRegs.GPADIR.bit.GPIO3 = 1; EDIS; } 

By default, our GPIOs are already configured as GPIOs, since all register values ​​are cleared, which means that "0" is already written to the GPAMUX1 register. For GPIO0 ... 11, the suspender is disabled by default, so we can only take and determine the direction of work on the exit using GPADIR. If you remember, the LEDs are connected to the controller by cathodes, which means that immediately after initialization they will glow. Let's right in the initialization function, we will configure these outputs to “1”:

 void InitLEDgpio (void) { EALLOW; GpioCtrlRegs.GPADIR.bit.GPIO0 = 1; GpioCtrlRegs.GPADIR.bit.GPIO1 = 1; GpioCtrlRegs.GPADIR.bit.GPIO2 = 1; GpioCtrlRegs.GPADIR.bit.GPIO3 = 1; EDIS; GpioDataRegs.GPASET.bit.GPIO0 = 1; GpioDataRegs.GPASET.bit.GPIO1 = 1; GpioDataRegs.GPASET.bit.GPIO2 = 1; GpioDataRegs.GPASET.bit.GPIO3 = 1; } 

As you can see, I do not use the GPADAT register for recording, but I use SET, CLEAR, TOGGLE . Also note that I made this entry outside the protected zone, that is, after the EDIS command . Now in the same function, configure GPIO12 to work with the button and add our function:

 void InitLEDgpio (void) { EALLOW; GpioCtrlRegs.GPADIR.bit.GPIO0 = 1; GpioCtrlRegs.GPADIR.bit.GPIO1 = 1; GpioCtrlRegs.GPADIR.bit.GPIO2 = 1; GpioCtrlRegs.GPADIR.bit.GPIO3 = 1; GpioCtrlRegs.GPAPUD.bit.GPIO12 = 1; GpioCtrlRegs.GPACTRL.bit.QUALPRD1 = 0xFF; GpioCtrlRegs.GPAQSEL1.bit.GPIO12 = 2; EDIS; GpioDataRegs.GPASET.bit.GPIO0 = 1; GpioDataRegs.GPASET.bit.GPIO1 = 1; GpioDataRegs.GPASET.bit.GPIO2 = 1; GpioDataRegs.GPASET.bit.GPIO3 = 1; } 

First of all, I turn off the internal tightening by writing “1” to the GPAPUD register , since it is enabled by default for GPIO12. As I wrote earlier, all the ports after initialization are configured for input, since zeros are written in the GPADIR register; we are not setting it up here.

It remains to configure the chatter protection, for this dividers write 0xFF , which corresponds to the value / 510. In the GPAQSEL1 register , write the value “10” or 2, which will set the value to a sample of 6 cycles. Done!In order to read the value of a specific input, you just need to read the value from the GPADAT register :

 if (GpioDataRegs.GPADAT.bit.GPIO12) { // Если кнопка нажата и подает +3.3В (лог. 1) на вход, то выполняем данные действия } 

This is how we interview the necessary conclusions. Now let's call the gpio configuration function in our main function and get its final look:

 void InitStartSystem (void) { DisableDog(); XtalOscSel(); InitPll(TMS320_PLLMUL, TMS320_DIVSEL); InitPeripheralClocks(); InitPieCtrl(); InitPieVectTable(); /*********************************/ InitLEDgpio(); } 

Now we call the InitStartSystem function in the main program body in main and this completes the setting. We get the following code:

 #include "main.h" int main (void) { InitStartSystem(); while(1) { } } 

It is time to write our first test program and check the whole thing. The algorithm is as follows: the LED that is sitting on GPIO3 blinks, and when you press a button on GPIO12, we simply light the GPIO0 LED. In this way, we will check the operation of the ports both at the entrance and at the exit. We write the following code:

 #include "main.h" int main (void) { InitStartSystem(); while(1) { if (GpioDataRegs.GPADAT.bit.GPIO12) { GpioDataRegs.GPACLEAR.bit.GPIO0 = 1; } else { GpioDataRegs.GPASET.bit.GPIO0 = 1; } GpioDataRegs.GPATOGGLE.bit.GPIO3 = 1; delay(100000); } } 

We compile, go to the debugger, launch it and see how one LED blinks constantly, and when the button is pressed another one lights up. At the end of the section I will attach a project with this code, if something does not work out, then look into it. Especially for those who are hard on texts or understood not all moments, I propose to watch this video for working with GPIO, everything happens there too, as in the “GPIO” section. I warn you that the video for an hour, dreary, long, but as much as possible and everything is clear:



Files from the article



Total


At this stage, I finish today's article. I think you understand that if I decided to immediately show the implementation of a DC / AC inverter, then the volume of the article would be several times larger, or many important little things would be left behind the scenes, which in my opinion is unacceptable.

I hope my article will help everyone to start mastering this family of controllers and begin development in the field of power electronics and machine tools. In the future I will probably write something else on this topic, for example, I would like to consider working with PWM or implement some kind of algorithm. The main thing is to have time.

If you still have any questions or something does not work out for you, then you can write me in private messages and I will try to answer your questions and provide all possible assistance in studying. Successes you in learning!

UPD.Thanks for the tip from BelerafonL to the book "Embedded high-performance digital control systems"

Source: https://habr.com/ru/post/410161/