<すぐに使えるSTM32HAL!>GPIO、Delay、ADC_DMA、UART(自信ない)、UART_DMA(自信ない)、内部Timer、PWM(Duty・周期可変)、SPI(途中)、I2C(まだ)、エンコーダ(A相、B相)、CAN(まだ)、Teratermの設定、floatの有効化、インデント、入力補完

GPIO

画像1

/* USER CODE BEGIN 3 */
# 3.3V印加、1
HAL_GPIO_WritePin(GPIO1_GPIO_Port, GPIO1_Pin, GPIO_PIN_SET);
# 0V印加、0
HAL_GPIO_WritePin(GPIO1_GPIO_Port, GPIO1_Pin, GPIO_PIN_RESET);
# 3.3Vと0Vの切り替え
HAL_GPIO_TogglePin(GPIO1_GPIO_Port, GPIO1_Pin);
# 3.3Vか0Vの読み取り
HAL_GPIO_ReadPin(GPIO1_GPIO_Port, GPIO1_Pin)

Delay

# ms待つ
HAL_Delay(300);

ADC_DMA

画像12

画像12

画像42

画像41

画像42

画像12

#include "main.h"
#include "stdio.h"
#include "string.h"

/* USER CODE BEGIN 2 */
uint32_t adc_v[2] = {0}; /* 0 - 4096 */
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_v, 2);
HAL_Delay(1000);
char s[256]; //UART参照

/* USER CODE BEGIN 3 */
sprintf(s, "%5lu, %5lu\n\r", adc_v[0], adc_v[1]);
huart2.gState = HAL_UART_STATE_READY;
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)s, sizeof(s));

UART

画像6

画像43

画像7

画像43


#include "main.h"
#include "stdio.h"
#include "string.h"

/* USER CODE BEGIN 0 */
char s[256];
void send_uart(char *s){
	HAL_UART_Transmit(&huart2, (uint8_t *) s, strlen(s), 2000);
}

/* USER CODE BEGIN 3 */
sprintf(buffer, "encorder = %d, CNT = %lu, DIR = %d\n", count_total, TIM1->CNT, (TIM1->CR1 >> 4) & 1 ? -1 : 1);
send_uart(buffer);
HAL_Delay(50);

UART_DMA

画像6

画像44

画像7

画像44

画像9

文字を送る場合。>>>%s
int >>> %d
int32_t >>> %ld
uint32_t>>>%lu
小数を送る場合。>>>%lf​

DMA送信完了と同時にUARTのSTATEをREADYにする。
extern を忘れない

画像26

main.cでif文を書く。

画像25

画像24

内部Timer

これは変数の設定が終わってから、最後にスタートするべき。

画像13

画像14

84MHz/4200/20000=1Hzの設定(APB1orAPB2)
念のためにオシロスコープで確認しよう。
Interruptにチェックを入れるのを忘れずに。

画像15

/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
HAL_Delay(1000);

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
 if (htim->Instance == htim2.Instance)
 {
	HAL_GPIO_TogglePin(GPIO1_GPIO_Port, GPIO1_Pin);
	sprintf(s, "%5lu\n\r", i);
	huart2.gState = HAL_UART_STATE_READY;
	HAL_UART_Transmit_DMA(&huart2, (uint8_t *)s, sizeof(s));
	i++;
 }
}

PWM(Duty比・周期可変)

画像35

画像37

/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);

/* USER CODE BEGIN 3 */
TIM2->CCR2 = 1000など;
TIM2->ARR = 2000-1など;
HAL_Delay(2000);

CCRxレジスタの値を変えれば、Duty比を変えられる。
ARRレジスタの値を変えれば、周期を変えられる。
100%のDutyを出すには、ARRレジスタの値よりCRRxレジスタの値が大きい必要があるとマニュアルに書かれてある。この例だと2000以上になる。
気になる場合は実際にオシロで確認するべき。

Duty比75%(ARR=1999、CRRx=1500)のとき

画像35

Duty比50%(ARR=1999、CRRx=1000)のとき

画像36

Duty比25%(ARR=1999、CRRx=500)のとき

画像37

周期の1/2(ARR=999、CRRx=500)のとき

画像38

周期の1/4(ARR=1999、CRRx=500)のとき

画像39

周期の1/8(ARR=3999、CRRx=500)のとき

画像40

SPI

画像19

SSがある場合、使用する前にhighからlowにする。
SCK以外は10kぐらいで必ずプルアップする。

画像20

画像21

MOSI--DI、MISO--DOに注意。

画像22

CPOL(Low)-->0
CPHA(1 Edge)-->0

画像23

アドレスと書き込み・読み込みの間にSSを挟むものと挟まないものがある。

I2C

Encorder

画像29

画像27

画像28

#include "main.h"
#include "stdio.h"
#include "string.h"

char buffer[1024];

void send_uart(char *s){
	HAL_UART_Transmit(&huart2, (uint8_t *) s, strlen(s), 2000);
}

int16_t get_encoder(void)
{
 static uint16_t count = 0;
 int16_t difference = 0;
 uint16_t nowcount = 0;
 uint8_t direction = 0;

 nowcount = TIM1->CNT;
 direction = (TIM1->CR1 >> 4) & 1 ? -1 : 1;

 if((nowcount*direction) < (count*direction)){
 	difference = nowcount - count + 65536*direction;
 	count = nowcount;
 }else{
	difference = nowcount - count;
	count = nowcount;
 }

 return difference;
}

/* USER CODE BEGIN 2 */
 int32_t count_total = 0;
 HAL_TIM_Encoder_Start( &htim1, TIM_CHANNEL_ALL);

/* USER CODE BEGIN 3 */
 count_total += get_encoder();
 sprintf(buffer, "encorder = %d, CNT = %lu, DIR = %d\n", count_total, TIM1->CNT, (TIM1->CR1 >> 4) & 1 ? -1 : 1);
 send_uart(buffer);
 HAL_Delay(50);

1回転させて、表示される値がパルス数の4倍になればOKのはず。
向きが逆の場合は、プログラムの±を書き換えてください。

画像35

画像30

DIRには向きが入ると書いてある。

画像31

Teratermの設定

画像10

画像11

printfにおけるfloatの有効化

画像12

インデント

Window>Preferences>General>Editors>Text Editorsの
Displayed tab widthを変更する。

Window>Preferences>C/C++->Code Style->Formatter
Newで変更する。

どっち?

入力補完

入力補完は、「ctrlとspace」で出来る。















この記事が気に入ったらサポートをしてみませんか?