CODE
The point of the NPC Inverter is to make a three-level PWM signal that gets filtered into a sine wave. To drive the output high, you turn on FETs 1 and 2; to keep the output at the neutral point, you turn on FETs 2 and 3; and to pull it to ground, you turn on FETs 3 and 4. As a result, if you want a sine wave, you will have two distinct modes of operation: one for “positive” voltages and one for “negative” voltages (positive and negative are with respect to the neutral point, not ground).
For positive voltages, the output PWM will be switching between high and neutral; as was mentioned before, driving the output high requires FETs 1 and 2 to be on, while neutral requires 2 and 3 to be on. So, in the positive half of the sine wave, FET 2 will always be on while FETs 1 and 3 will alternate (Figure 2). Conversely, in the negative half of the sine wave, FET 3 will always be on while FETs 2 and 4 will alternate (Figure 3).
To implement this idea, we needed 4 PWM channels coming out of the microcontroller chip (MCU) for each phase we wanted to create. Since we have 3 phases, we needed 12 in total. We also wanted to implement three ADC channels so that we would have the possibility to extend the design by adding in closed-loop feedback in case the input voltage was changing.

Figure 1: One phase of an NPC Inverter

Figure 2: Inputs required for a positive output duty cycle
Challenges
Because we needed 12 PWM channels, we couldn’t use the standard Green Electronics libraries; so, we decided to use a new toolchain that ST had developed that centered around a new program called STM32CubeMX. Cube, as we’ll call it, is a graphical way to generate initialization code for the STM32 series of microcontrollers. In general, it worked very well for some libraries but not as well for others; creating PWM channels was made extremely easy, while ADC channels still took quite a bit of time.
Cube is also good at interfacing with a few different IDEs that can be used to work with their boards. Because Cube is built around a newer set of drivers than the Green Electronics libraries, we couldn’t mix code between the two libraries; so, we decided to start from scratch and use the IDEs that Cube interfaces with easily. We eventually settled on Atollic TrueStudio Lite, which is an Eclipse-based IDE that is free and offers fairly good debugging tools.
After fixing all the various issues around creating a simple Hello World program (which in our case was the STM32 Discovery Demo that comes with the driver package for the STM32F3 Discovery board), we started work on our own code.

Figure 3: Inputs required for a negative output duty cycle

Figure 4: STM32CubeMX

Figure 5: TrueStudio Lite is open on the left and PuTTy, our USART interface program, is open on the right
Implementing PWM
Implementing PWM was the easiest of all the features we tried to do. If you configure Cube correctly, it generates almost all the code you need. We configured Timers 1, 3, and 4 to have four PWM channels each; once that was done, we needed to manually add code that starts the timers. Afterwards, the only thing we ever needed to change was the duty cycle of the PWM signals; this was trickier, since none of the Hardware Abstraction Layer (HAL) drivers that Cube is built off of can do that on their own. Luckily, ST also includes Low Level (LL) drivers; the content of the LL function to change the duty cycle is simply a line to write the CCR register of that timer’s channel, so we took that line and created our own function to change the duty cycle of a certain channel of a certain timer.
Once we could set the duty cycle, we realized that we needed to make sure that when operating to produce a positive voltage, we needed channel 3 to be low when channel 1 was high and vice versa. To implement that, we switched the polarity of channels 2 and 3 in Cube, which meant that when channels 2 and 3 were a logical 1, they would be at 0 V, and when they were a logical 0, they would be at 3 V. The result of all that work is shown in Figures 6 and 7, which depict duty cycles of 0.5 and -0.5 respectively.

Figure 6: Inputs for one phase leg with a duty cycle of 0.5

Figure 7: Inputs for one phase leg with a duty cycle of -0.5
Finally, we wanted to implement some dead time between when FETs 1 and 3 switched; because of the way the diodes are oriented, turning on the top three FETs would short the high voltage to the neutral voltage, which would run too much current through our FETs (this also applied to FETs 2 and 4 when using a negative duty cycle). Since only Timer 1 had dead time as an option, we decided to shorten the on-time of FETs 2 and 3 by 250 ns whenever they were switching; we were operating the timers in center-aligned mode, so the 250 ns were split evenly between the two ends of the on-time which resulted in Figure 8.

Figure 8: Dead time inserted between turning off FET 3 and turning on FET 1 was ~125 ns.
Implementing USART
USART itself is surprisingly easy to implement with the HAL drivers, since there is a function specifically made to transmit a string. However, we also wanted to get the printf() function working well, which was more complicated. To do so, we found an example that used the STM32F1, took its syscalls.c file, and then added some modifications to make it more understandable and work better for the STM32F3, which we were using.
USART allows you to use a serial port to transmit data from your computer to the microcontroller and vice versa. The HAL drivers are very useful for this; the first step in any implementation of USART should be to use them to transmit a single character, then a sentence, and then build off of that. But, before you can even start doing that, you have to set up the USART to match your serial port.
The main options you have to configure are Baud Rate (the speed at which symbols are transmitted through the line), Word Length, and Parity. For our implementations, we set Word Length to 8 bits and Parity to none (rather than odd or even). We began with a baud rate of 9600, which is the standard on most computers; because that wasn’t fast enough, we moved to 115200.
To interface with the microcontroller, we used PuTTy, a free program that can be found here: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html. In the Code section of our Github, you’ll find a .txt file named “Notes on MCU Setup” that has instructions on how to set up PuTTy with our settings and a baud rate of 9600.
After all of that was set up, we began to transmit characters and eventually sentences from the board to our computer. The next step was to implement printf(), which is a function that makes it easy to print floats, ints, and other numbers. The printf() function calls other functions that are built into the C libraries; in order to get it to work with USART, you have to modify those. Specifically, for printf() you have to modify the _write() function to call the HAL_UART_Transmit() function; based on our current understanding, this modification must take place in a file called "syscalls.c". Unfortunately, you can’t call HAL_UART_Transmit() in the syscalls.c file because you don’t have access to the uart object there; so, you have to add an external function that we called putstr() and then define that function inside your main.c file, where you will have access to the uart object. Again, the “Notes on MCU Setup.txt” file has instructions on what bits of code to add to the main file in order to get that to work.
Once all of that is finished, the end result is that you can use printf like it should normally be used and see the result through PuTTy! The only bug we’ve found so far is that you can’t put “\r\n” in the beginning of your string, because then printf will stop after the new line; that wasn’t an issue for us, though, so we left it as is.
Implementing ADC
We planned on implementing closed-loop control in our system by reading our input voltage values and adjusting our PWM switching accordingly. The motivation for this was so that we can tie our inverter to an array with MPPT and the grid. To accomplish this, we needed to implement ADC functionality in our embedded code. Although the libraries used in the course had ADC functionality, we had to scrap those libraries in order to access custom channel and timer configurations. We built our code base around the STM32 HAL (Hardware Abstraction Level) libraries. This ended up being rather cumbersome and tedious for ADC especially because of the complexity of the HAL ADC functions. We used a couple online resources to guide our work with ADC. The STM manuals were not very useful because they did not give implementation examples and were very high level. We found that the libraries themselves, along with the comments, were very helpful. Sites with guides also pushed us along. The most useful was this one: http://visualgdb.com/tutorials/arm/stm32/adc/.
To run a 3 channel ADC, we needed to initialize an interrupt timer, the three GPIO pins, and DMA functionality. This also meant creating a series of buffers to store and move the conversions around between functions. We also had to write a series of cascading callback functions that would be triggered every time an ADC conversion finished. It would ensure that the values were stored in the correct location in memory and that the values were transferred to buffers for us to process and use in real time printing and control. Our toolchain for this also failed us because of the incompleteness of the code generated by STM32CubeMX. For ADC initialization, the code only got us halfway there. It required additional HAL functions oftentimes. We ultimately had ADC functionality demonstrated on the board, before IDE and configuration pathway issues prevented us from debugging anymore.