デバッグ用に1つだけ送信だけのUARTを使います。
方針 1
デバッグ用に1つだけ送信だけのUARTを使うためだけに余計なことはしません。
NUCLEO G474REには最初からLPUART1が使えるようになっています。
これがおそらくピン(PA2/PA3)とつながっていません。
USB経由で出力することになり、VCPを使うことになり、STが公式で配布しているVCP DriverはWindows用のみなので、Ubuntuだと実現不可か死ぬほど面倒かのどちらかになりそうです。
Clockも調整が必要になる可能性も高いので避けたいです。
最終的には電源はUSBではない予定で、開発中はモバイルバッテリーを使うことが多くて、TX/RX/GNDを外に出してFTDI UART-USBでRaspberry Pi 4で受けると簡単です。
Raspberry Pi 4で2つUARTを受けるはよくやっています。Raspberry Pi 3でやると実現不可か死ぬほど面倒かのどちらかになると思います。
UART4よりも、USART1の方が動く可能性が高そうと考えます。
方針 1 : USART1を使う。
方針 2
サンプルにprintfがありますが、printfは、やりません。
組み込みでは、printfが使えるように時間を使うよりも、sprintfやsnprintfに慣れた方が仕事が早いと考えるからです。
方針 2 : printfは無視する。
方針 3
CPUに無駄な苦労をさせないためにDMAを使います。
しかし、DMAはHAL Driverに昔から問題があり、直っていないこともある、ということなので、上手くいかなかったら、すぐやめます。
最終的にはUARTは不要なので。
方針 3 : UART + DMAは実現させたいが、上手くいかなかったらすぐに止める。
UARTの設定
USART1を選択して、ModeをAsynchronousにします。
RX用とTX用のDMAをAddします。基本的にハードウェア構成の変更は面倒なので、先にここで完成形にしておきます。
どちらもModeをCircularにします。
NVICに自動的にチェックが入っています。
NVICはARMの安いSoCの割り込みコントローラーでよく使われています。
GPIO SettingsからTX/RXをpullupします。
設定するためのインターフェースが見えません。
かなり上に引っ張ってきて、やっと見えるようになります。これはひどい。
Project Managerを確認しておきます。Advanced Settingsを確認します。
DMA_InitがUSART1_UART_Initよりも上にあることを確認します。
これが逆だと問題が起きることがあるそうです。
これをクリックします。
ハードウェア構成から、ソフトウェアを生成させます。
Core >> Src >> main.c
選択して、F2を押します。main.cppに変更します。これで、gccではなくg++が使われるようになります。
最初にC++って選択したじゃん、って思うのですが。
現状、必ずmain.cを生成すると思います。(2021.12.03)
自分の場合
ソフトウェアを編集するときに、IDEを使わずにviを使う人のための設定。
$ cd
$ ln -s STM32CubeIDE/workspace_1.7.0 ide
名前(上記ではide)はなんでもいいのですが、これでcd i <tab>で開発環境にすぐに行けるようになります。
関数の定義に飛べるようにします。
インストールされていないときにctagsを実行すると、Ubuntuが
sudo apt install exuberant-ctags
sudo apt install universal-ctags
のどっちかをやってくださいと教えてくれます。自分が使う範囲ではexuberant-ctagsで問題になることはないですがuniversal-ctagsの方が良いらしいです。
下記の操作だとexuberant-ctagsがインストールされます。
$ cd ~/ide/g474re_sensor
$ ctags -R (怒られる)
$ sudo apt install ctags
$ ctags -R
ソフトウェア開発を始めるにあたり
最初にライセンスを確認します。
さすがにGPLではないですね。一応、安心しておきます。
どこから漁るか
本家STがソフトウェアを公開していますので、ここから漁ります。
STMicroelectronics/STM32CubeG4
Examples | uses only the HAL and BSP drivers (Middleware not used) |
Examples_LL | uses only the LL drivers (HAL and Middleware not used) |
Examples_MIX | uses only HAL, BSP and LL drivers (Middleware are not used) |
Applications | intends to demonstrate the product performance and how to use the different Middleware stacks available |
Demonstrations | aims to integrate and run the maximum of peripherals and Middleware stacks to showcase the product features and performance |
情報元。HALとLLとBSPの関係が図で描かれています。
STM32CubeG4GettingStarted.pdf
STM32CubeProjectsList.html
よくやるやり方としてDemonstrations、Applicationsにあれば最良ですが、STM32の場合はHALかLLみたいです。Examples、Examples_LL、Examples_MIX、の順でしょうか。
UART without DMA
ソースコードです。ほぼサンプルコードのままです。
main.cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2021 STMicroelectronics.
* All rights reserved.
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef hlpuart1;
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;
/* USER CODE BEGIN PV */
char uart_message[256] = {0};
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_LPUART1_UART_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
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_LPUART1_UART_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
sprintf( uart_message, "\r\n ----- UART TEST ----- \r\n" );
HAL_UART_Transmit(&huart1, (uint8_t *)uart_message, 64, 5000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST);
/** 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 = RCC_PLLM_DIV4;
RCC_OscInitStruct.PLL.PLLN = 85;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
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_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
/** Initializes the peripherals clocks
*/
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_LPUART1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief LPUART1 Initialization Function
* @param None
* @retval None
*/
static void MX_LPUART1_UART_Init(void)
{
/* USER CODE BEGIN LPUART1_Init 0 */
/* USER CODE END LPUART1_Init 0 */
/* USER CODE BEGIN LPUART1_Init 1 */
/* USER CODE END LPUART1_Init 1 */
hlpuart1.Instance = LPUART1;
hlpuart1.Init.BaudRate = 115200;
hlpuart1.Init.WordLength = UART_WORDLENGTH_8B;
hlpuart1.Init.StopBits = UART_STOPBITS_1;
hlpuart1.Init.Parity = UART_PARITY_NONE;
hlpuart1.Init.Mode = UART_MODE_TX_RX;
hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
hlpuart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&hlpuart1) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&hlpuart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&hlpuart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&hlpuart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN LPUART1_Init 2 */
/* USER CODE END LPUART1_Init 2 */
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMAMUX1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* DMA1_Channel2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LD2_Pin */
GPIO_InitStruct.Pin = LD2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
/* 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 */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
変更点は下記です。4行のみです。
#include <stdio.h>
char uart_message[256] = {0};
sprintf( uart_message, "\r\n ----- UART TEST ----- \r\n" );
HAL_UART_Transmit(&huart1, (uint8_t *)uart_message, 64, 5000);
sprintfを使うためにstdio.hをincludeしています。
HAL_UART_TransmitがSTが提供しているHALライブラリです。
注意点は下記です。Renesasも同様に書く場所を指定してきますが、STのは慣れるまで時間がかかる気がします。
/* USER CODE BEGIN XXXXX */
ここに自分のソースコードを書く。
/* USER CODE END XXXXX */
C++を使うとき、小賢しいですが下記で負担を減らすことができます。
cd Core/Src
cp main.cpp main.c
mv main.cpp ~/main.cpp.backup
アイコンをクリックしてソースコード生成
(左ペインのSrcを選択してF5)
左ペインのmain.cを選択してF2
Ctrl + ‘b’か、Runでコンパイルする。
Program Download
USBでMacとNUCLEO G474REを接続します。
[Run]をクリックします。
余計なものを選択していて上手くいかない場合などは、下記を選択します。
Binaries >> g474re_sensor.elf
下記のポップアップが出てきます。
初回はST-LINKのFirmwareのupdateをしろ、と出てくるでしょう。
Yes
Open in update mode
Upgrade
Ubuntu 20.04.3だと怒られます。
STのCommunityに怒られなくする方法が書いてあります。
How can i fix “Could not determine GDB version using command: ” error ?
$ sudo apt install libncurses5*
[Y/n] Y
成功 !!
Release Buildでも結構デバッガーが止まるのがSTM32
Debug Buildは実行速度が遅いので、Release Buildにします。
[Project] >> [Properties] >> [C/C++ Build]
Configuration
必ず[Manage Configuraitons]をクリックしてください。そして、Releaseを選択してSet Activeをクリックしてください。そうしないと設定が保存されません。
結構色々なところで設定が保存されませんので注意が必要です。
検証環境としてRaspberry Pi 3 Model B+を使う
ディスプレイが用意されている場合、最も手っ取り早いであろうRaspberry Pi 3を使います。
物理的なワークスペースが必要です。マウスとキーボードも安物でも用意しておく必要があります。
UARTでは簡単すぎて不要ですが、I2CやSPIとなるとロジックアナライザーは必須です。このとき、信号を横取りするためにブレッドボードも必須です。
オシロスコープもあると良いです。
USART1
PC4 : TX
PC5 : RX
TX/RX/GNDを正しく接続します。
NUCLEO G474REのTXと、Raspberry Pi 3 Model B+のRX、
NUCLEO G474REのRXと、Raspberry Pi 3 Model B+のTX、
NUCLEO G474REのGNDと、Raspberry Pi 3 Model B+のGND、
を接続します。
uart.py
import time
import serial
uart = serial.Serial('/dev/ttyAMA0', 115200, timeout=0.5)
while True:
# time.sleep(0.01)
# time.sleep(0.02)
# time.sleep(0.03)
# time.sleep(0.05)
# time.sleep(0.1)
time.sleep(0.2)
print( uart.readline().decode('utf-8') )
# print( uart.readline() )
# print( uart.read() )
‘utf-8’ codec can’t decode byteと怒られる場合もありますが、何回かやっていると通るはずです。
予備は大事
Raspberry Pi 3 Model B+ですが、昨日は何の問題もなく動いていたのに、今日は起動しない、ということが何回かありました。
電源ONにして赤のLEDは点灯するけど、緑のLEDが全く点灯しないのでOSが起動していないとわかります。
Raspberry Pi 3は2つ以上、SDカードは3枚以上もっておくと安心できます。SDカードは同じものにしておきましょう。カードリーダーも2個以上用意しておきましょう。
Macだと、dfで確認して、クローン元のSDカードが/dev/disk2s1で、クローン先のSDカードが/dev/disk3s1とします。
% df
/dev/disk2s1 43233 22041 21192 51% 0 0 100% /Volumes/boot
/dev/disk3s1 258095 53978 204117 21% 0 0 100% /Volumes/boot 1
アンマウントします。
diskutil umount /dev/disk2
or
diskutil umount /dev/disk2s1
diskutil umount /dev/disk3
or
diskutil umount /dev/disk3s1
raw diskの方が速いとのことです。
% sudo dd if=/dev/rdisk2 of=/dev/rdisk3 bs=1m
128GBのSDカードで30分強くらいでした。クローン元だと起動しませんが、クローン先だと起動します。
接触不良か、クローン(コピー)はできるけどRaspberry Pi 3が起動できないくらいの破損か、どちらにしても勘弁してほしいです。
置き換える
通信が確立して、ロジックアナライザーが絶対に不要となったらFTDI UART-USBに置き換えてしまいましょう。
LPUART1を使おうとしたとき、ロジックアナライザーで見ると波形が全く動かないはずです。見える化は大事。
まとめ
瞬殺ですね。
これが瞬殺でなかったら、もうSTの製品は買いません。
参考
Logic analyzer
入門用に3000円未満で色々と出来ます。
凄い使いやすいというものではないです。
USBケーブルが断線しやすいので、最初から別の市販品を使用した方が時間の無駄が無くて良いです。
広告
IT開発関連書とビジネス書が豊富な翔泳社の通販『SEshop』さくらのレンタルサーバ
ムームードメイン
Oisix(おいしっくす)
らでぃっしゅぼーや
珈琲きゃろっと
エプソムソルト