STM32 Nucleo-F746ZG Keil
Overview
This tutorial is a step-by-step guide on how to build a Web UI dashboard on NUCLEO-F746ZG development board using Keil development environment. Once the dashboard is up and running, it is trivial to extend it using other examples - for example, to add MQTT connectivity, etc.
Step 1: Skeleton
In this step, we create a project skeleton. Run Keil, and
- Create a new project
- Select STM32F746ZG as an architecture
- Choose the following components:
- Board support / LED
- CMSIS / Core
- Device / Startup
- STM32Cube Framework API / Classic
- Click on "Resolve" to resolve dependencies, click "OK"
- In the Project / Target1 / SourceGroup1, add "main.c" file, copy-paste and save:
#include "RTE_Components.h"
#include "stm32f7xx_hal.h"
#include "Board_LED.h"
int main(void) {
HAL_Init();
SystemCoreClockUpdate();
LED_Initialize();
LED_On(2);
while (1) {}
}
- In the IDE toolbar, select Options / Debug tab, choose ST-Link debugger, enable "Download to flash". Click "Flash Download" tab, enable "Reset and Run".
- Select "Target" tab, enable "Use MicroLib"
- Select "C/C++" tab, set "Warnings" to "MISRA Compatible"
- Click "Build", then "Load". The board should turn red LED
Step 2: Add RTOS
Now, we'll add RTOS support and create a blinking task. Later on, we transform the blinking task into a web server task.
- Click on Run-Time Environment and enable the CMSIS / RTOS2 / Keil RTX5
- In the project tree, open Device /
startup_stm32f746xx.s
file - Find a "HeapSize" line and increase heap size, then save:
Heap_Size EQU 65536
- Update main.c to the following code:
#include "RTE_Components.h"
#include "stm32f7xx_hal.h"
#include "Board_LED.h"
#include "cmsis_os2.h"
static void mytask(void * param) {
LED_Initialize();
LED_On(2);
int on = 0;
for (;;) {
on = !on;
on ? LED_On(1) : LED_Off(1);
osDelay(200);
}
}
int main(void) {
HAL_Init();
SystemCoreClockUpdate();
osKernelInitialize ();
size_t stack_size = 4096;
void *stk = malloc(stack_size);
const osThreadAttr_t attr = { .stack_mem = stk, .stack_size = stack_size };
osThreadNew(mytask, NULL, &attr);
osKernelStart();
}
- Click on Build, then Load. The board should have blue LED blinking
Step 3. Update system clock
At this step we simply update system clock to run the board at maximum speed of 216 MHz.
- Add a new file
init.c
to the Source Group 1, with the following content:
#include "stm32f7xx_hal.h"
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/** Configure LSE Drive Capability
*/
HAL_PWR_EnableBkUpAccess();
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 216;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
/** Activate the Over-Drive mode
*/
HAL_PWREx_EnableOverDrive();
/** 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_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART3|RCC_PERIPHCLK_CLK48;
PeriphClkInitStruct.Usart3ClockSelection = RCC_USART3CLKSOURCE_PCLK1;
PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48SOURCE_PLL;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
}
- Modify
main.c
- add two extra lines inmain()
:
int main(void) {
HAL_Init();`
extern void SystemClock_Config(void) // Add this line
SystemClock_Config(); // And this line
SystemCoreClockUpdate();
...
Clink on Build, Load. An LED should continue to blink.
Step 4. Add networking
Click on Run-Time Environment and enable the following:
- Network / Core
- Network / Socket / BSD
- Network / Socket / TCP
- Network / Socket / UDP
- CMSIS Driver / Ethernet MAC (API) / Ethernet MAC
- CMSIS Driver / Ethernet PHY (API) / LAN8742A
- Network / Interface / ETH (set to 1)
Click on Resolve, then OK
In the Project explorer, click on "Device / RTE_Device.h" to open it
Choose "Configuration Wizard" tab at the bottom
Enable ETH and make sure it looks like this:
In the Project explorer, click on "Network / Net_Config_BSD.h" to open it, click on "Configuration Wizard" tab at the bottom. Increase the number of BSD sockets to 10 and listening sockets to 3. It is important to have a number of listening sockets minimum 3 (because 3 is the default Mongoose's
MG_SOCK_LISTEN_BACKLOG_SIZE
), otherwiselisten()
socket syscall is going to fail and nothing will work:Similarly, change "Network / Net_Config_TCP":
And "Network / Net_Config_UDP":
In the main.c, add the
rl_net.h
include and the call tonetInitialize()
:
#include "RTE_Components.h"
#include "stm32f7xx_hal.h"
#include "Board_LED.h"
#include "cmsis_os2.h"
#include "rl_net.h"
static void mytask(void * param) {
LED_Initialize();
netInitialize();
LED_On(2);
int on = 0;
for (;;) {
on = !on;
on ? LED_On(1) : LED_Off(1);
osDelay(200);
}
}
int main(void) {
HAL_Init();
extern void SystemClock_Config(void);
SystemClock_Config();
SystemCoreClockUpdate();
osKernelInitialize ();
size_t stack_size = 4096;
void *stk = malloc(stack_size);
const osThreadAttr_t attr = { .stack_mem = stk, .stack_size = stack_size };
osThreadNew(mytask, NULL, &attr);
osKernelStart();
}
- Build, Load. The blue LED should continue blinking
- At this point, we have our board running TCP/IP stack, but no networking application is using it.
Step 5. Add mongoose
- Start your browser and download mongoose.h and mongoose.c directly into the project's directory
- In the project explorer, SourceGroup1, choose "Add existing item" and add both mongoose.c and mongoose.h
- Right-click on SourceGroup1, select Options, choose "C/C++" tab, paste this in the "Misc Controls" to enable RTX support in Mongoose:
-DMG_ARCH=MG_ARCH_RTX
- Add a very simple Web server. Modify main.c to the following:
#include "RTE_Components.h"
#include "stm32f7xx_hal.h"
#include "Board_LED.h"
#include "cmsis_os2.h"
#include "rl_net.h"
#include "mongoose.h"
uint64_t mg_millis(void) { // Declare our own uptime function
return osKernelGetTickCount();
}
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) {
mg_http_reply(c, 200, "", "%s\n", "hi");
}
}
static void mytask(void * param) {
LED_Initialize();
netInitialize();
LED_On(2);
int on = 0;
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0:80", fn, &mgr);
for (;;) {
on = !on;
on ? LED_On(1) : LED_Off(1);
mg_mgr_poll(&mgr, 200);
}
}
int main(void) {
HAL_Init();
extern void SystemClock_Config(void);
SystemClock_Config();
SystemCoreClockUpdate();
osKernelInitialize ();
size_t stack_size = 4096;
void *stk = malloc(stack_size);
const osThreadAttr_t attr = { .stack_mem = stk, .stack_size = stack_size };
osThreadNew(mytask, NULL, &attr);
osKernelStart();
}
- Build, Load. The blue LED should continue to blink
- In order to find what IP address a DHCP server has assigned to our board, we could ping a broadcast address, or check the ARP table on a router. In our case, it is 192.168.2.22 - could be different in your case
$ arp -an
? (192.168.2.22) at 1e:30:6c:a2:45:5e on bridge100 ifscope [bridge]
- Point your browser at http://192.168.2.22, see the "hi" message
- We have a very basic web server up and running!
Step 6. Add Web UI
Let's add a more advanced Web UI to our web server.
- Download two files to the project folder - they are both from the Mongoose dashboard example: packed_fs.c and web.c One contains embedded UI files, another one has a corresponding event handler function
- Add those two files to the SourceGroup1 as existing files
- Right-click on SourceGroup1, select Options, choose "C/C++" tab, paste this in the "Misc Controls":
-DMG_ARCH=MG_ARCH_RTX -DMG_ENABLE_PACKED_FS=1
- Modify main.c to the following:
#include "RTE_Components.h"
#include "stm32f7xx_hal.h"
#include "Board_LED.h"
#include "cmsis_os2.h"
#include "rl_net.h"
#include "mongoose.h"
uint64_t mg_millis(void) { // Declare our own uptime function
return osKernelGetTickCount();
}
static void mytask(void * param) {
LED_Initialize();
netInitialize();
LED_On(2);
int on = 0;
struct mg_mgr mgr;
mg_mgr_init(&mgr);
extern void device_dashboard_fn(struct mg_connection *, int, void *, void *);
mg_http_listen(&mgr, "http://0.0.0.0:80", device_dashboard_fn, &mgr);
for (;;) {
on = !on;
on ? LED_On(1) : LED_Off(1);
mg_mgr_poll(&mgr, 200);
}
}
int main(void) {
HAL_Init();
extern void SystemClock_Config(void);
SystemClock_Config();
SystemCoreClockUpdate();
osKernelInitialize ();
size_t stack_size = 4096;
void *stk = malloc(stack_size);
const osThreadAttr_t attr = { .stack_mem = stk, .stack_size = stack_size };
osThreadNew(mytask, NULL, &attr);
osKernelStart();
}
- Build, Load
- Wait a couple of seconds until DHCP assigns address, then point your browser at the assigned IP, see the dashboard UI