Как то совпало, что с момента начала моего более серьезного ковыряния STM32, ST electronics решили отказаться от библиотеки SPL, которую я использовал в предыдущих статьях. Думаю инфа не первой свежести. Тем не менее, ковырять новое детище решил не сразу, поэтому в последнее время просто наблюдал за развитием ситуации.

Для тех кто не в курсе. На текущий момент для инициализации и работы периферии имеются следующие варианты:
1. Использовать регистры (CMSIS)
2. Продолжать использовать SPL
3. Использовать HAL
4. Изобретать свой велосипед

1. В принципе идея такая же, как используется в AVR, т.е. есть портD нужно включить 12 ножку, то делаем так:

GPIOD->BSRRL = GPIO_BSRR_BS_12;

Но это еще более менее понятная запись, обычно начинающим выносят мозг такие записи:

RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;

Повторюсь, подобный же подход используется в AVR, там с этим нет проблем, ибо настроек не так много как у STM. В целом подобный подход подразумевает чтение даташита и все бы хорошо, но проблема в том, что его практически никто не читает, кроме того нет переносимости кода между разными сериями.

Тем не менее, решил испробовать. Честно открыл даташит, нашел страницу с описанием инициализации и написал код. Не заработало, причина не важна, сама суть в том, что пришлось гуглить и ковыряться внутренностях той же SPL, чтобы понять проблему. В итоге оказалось, что информация разбросана по даташиту в нескольких местах, поэтому требуются танцы с бубном.

С точки зрения общего развития это хорошо, много нового узнал, но конкретно в тот момент нужно было по быстрому родить простой кусок кода и вместо 5 минут потратил около часа. Конечно в следующий раз, ковыряться уже не нужно будет, но могут быть и более сложные вещи на изучение которых можно потратить намного больше времени. В общем данное занятие на любителя.

2. Хоть SPL и не рекомендуется к употреблению, но с ее использованием написано уже много примеров и она есть для основных для меня 103 и 407 серии. Ее подход мне вполне понятен, на ней проще учиться. Для рядового радиолюбителя, который планирует использовать ограниченный потенциал микроконтроллера в принципе большего и не надо. Но опять же столкнулся с тем, что перенос кода между 103 и 407 требует внимательной правки ручками и долбления в даташит.

3. Новое детище ST, которое пришло на смену SPL. Где то видел мнение, что HAL это обертка над SPL. Ради интереса поковырялся, некоторые куски действительно похожи, но в большинстве своем совершенно разные. Хорош HAL тем, что он используется с Cube Mx — генератором кода для STM, некий аналог CAVR для AVR, т.е. проект можно сгенерить парой кликов мышки. Тогда почему просто не использовать HAL? Проще всего ответить на этот вопрос так: сгенерируй проект и посмотри на код, если нет рвотных позывов, то милости просим. Лично по мне, наворочено много лишнего, там где это не нужно, хотя это скорее дело привычки.

4. Ну и тем кому не нравятся библиотеки от ST изобретают свой велосипед. Вероятно, если бы я работал только с одной серией и рожал много прошивок, то выбрал бы именно этот подход.

В итоге, можно считать, что ситуация закончилась победой HAL, ибо Keil и Cocox полностью избавились от SPL. Понемногу появляются статьи с использованием HAL. Но в общем то кто как хочет, тот так и .. программирует. Честно сказать, то что генерирует Cube Mx это нечто монстроидальное, тяжело тупить в такой код.

Тем не менее, решил потихоньку вливаться в мейнстрим, исключительно ради спортивного интереса. Но чтобы не начинать снова со 100500 мигания светодиодом, затарился парочкой простых микросхем. Первой из них и самым простым будет цифровой резистор AD8400. Почему именно он? Да хз, что было то и взял.

Вкратце что это такое? Похоже очень на простой резистор, у которого есть два крайних вывода A1 и B2 между которыми постоянное сопротивление, и щетка W1 положение которой мы можем изменять.
ad8400

Управляется он по SPI протоколу. Поэтому я подключил его так, чтобы с W1 можно было снимать напряжение:
ad8400

Тот резистор, что есть у меня одноканальный, т.е. в одном корпусе один резистор, но есть такие же с двумя и четырьмя резисторами в одном корпусе. Бывают на 1, 10, 50, 100кОм. Хоть и было заявлено номинальное сопротивление 10кОм, на практике оно оказалось 8кОм, прозвонить его можно только когда подано питание. После включения на щетке будет рандомное значение.

Управление хоть и SPI, но по факту железный spi задействовать не удастся, ибо вначале идут 2 бита адреса, а затем 8 бит данных. В итоге за один раз нужно передавать 10бит. Весь диапазон резистора разбит на 256 значений, для 10кОм шаг будет в ~40 Ом.

Алгоритм работы будет следующий:
1. Прижимаем ножку CS к земле
2. С каждым тактом CLK выдаем 2 нуля на ножке MOSI
3. C каждым тактом CLK выдаем 8 бит данных на ножке MOSI
4. Поднимаем ножку CS

Для управления использовал программный spi. Сгенерил проект в Cube — тупо настроил PB13-PB15 как выходы. Лень было заморачиваться, поэтому сразу после включения, однократно посылаем байт на резистор. Для желающих можно, включить фантазию, например регулировать уровень звука с помощью энкодера или кнопки. Привожу два варианта кода, для SPL и HAL исключительно для сравнения, в последующих статья планирую юзать только HAL. Исходники прикреплять не буду, ибо 35мб для пустого кода больно жирновато. Тестировалось на STM32F4Discovery.

Для SPL

#include <stm32f4xx.h>
#include <stm32f4xx_gpio.h>
#include <stm32f4xx_rcc.h>
 
//1.B1 - GND
//2.GND
//3.CS - PB14
//4.SDI(MOSI) - PB15
//5.CLK - PB13
//6.VDD -
//7.W1 - test point
//8.A1 - VCC
 
// режим SPI / SPI MODE = 0
// назначение выводов порта
#define SPI_SCK GPIO_Pin_13  // выход - SCLK
#define SPI_SDI GPIO_Pin_15  // выход - MOSI
#define SPI_SS  GPIO_Pin_14  // выход - Chip Select
#define SPI_PORT GPIOB // порт spi
 
void _spi_start(void)
{
   GPIO_ResetBits(SPI_PORT,SPI_SCK); // SPI_MODE = 0
   GPIO_ResetBits(SPI_PORT,SPI_SS); // Chip Select - Enable
}
 
void _spi_stop(void)
{
   GPIO_SetBits(SPI_PORT,SPI_SS); // Chip Select - Disable
   GPIO_ResetBits(SPI_PORT,SPI_SCK); // SPI_MODE = 0
}
 
void _spi_init(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
 
GPIO_InitTypeDef s_SDI;
s_SDI.GPIO_Mode = GPIO_Mode_OUT;
s_SDI.GPIO_OType = GPIO_OType_PP;
s_SDI.GPIO_Pin = SPI_SDI;
s_SDI.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(SPI_PORT, &s_SDI);
 
GPIO_InitTypeDef s_SCK;
s_SCK.GPIO_Mode = GPIO_Mode_OUT;
s_SCK.GPIO_OType = GPIO_OType_PP;
s_SCK.GPIO_Pin = SPI_SCK;
s_SCK.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(SPI_PORT, &s_SCK);
 
GPIO_InitTypeDef s_SS;
s_SS.GPIO_Mode = GPIO_Mode_OUT;
s_SS.GPIO_OType = GPIO_OType_PP;
s_SS.GPIO_Pin = SPI_SS;
s_SS.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(SPI_PORT, &s_SS);
}
 
void _spi_sendbyte(unsigned char a)
{
   unsigned char i;
 
   // отправить адрес 00
   for(i=0; i<2; i++)
   {
      GPIO_ResetBits(SPI_PORT,SPI_SDI); 
      GPIO_SetBits(SPI_PORT,SPI_SCK); 
      GPIO_ResetBits(SPI_PORT,SPI_SCK); 
   }
   for(i=0; i<8; i++)
   {
      if (a & 0x80)
      {
    	  GPIO_SetBits(SPI_PORT,SPI_SDI); 
      }
      else
      {
    	  GPIO_ResetBits(SPI_PORT,SPI_SDI); 
      }
      GPIO_SetBits(SPI_PORT,SPI_SCK); 
      a <<= 1;    
      GPIO_ResetBits(SPI_PORT,SPI_SCK); 
   }
}
 
int main(void)
{
_spi_init();
_spi_start();
_spi_sendbyte(127);
_spi_stop();
 
while (1);
}

С использованием HAL

#include "stm32f4xx_hal.h"
 
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
 
#define SPI_SCK GPIO_PIN_13  //SCLK
#define SPI_SDI GPIO_PIN_15  //MOSI
#define SPI_SS  GPIO_PIN_14  //Chip Select
#define SPI_PORT GPIOB //spi
 
void _spi_start(void)
{
   HAL_GPIO_WritePin(SPI_PORT,SPI_SCK,GPIO_PIN_RESET);
   HAL_GPIO_WritePin(SPI_PORT,SPI_SS,GPIO_PIN_RESET); 
}
 
void _spi_stop(void)
{
   HAL_GPIO_WritePin(SPI_PORT,SPI_SS,GPIO_PIN_SET); 
   HAL_GPIO_WritePin(SPI_PORT,SPI_SCK,GPIO_PIN_RESET);
}
 
void _spi_sendbyte(unsigned char d)
{
   unsigned char i;
   for(i=0; i<2; i++)
   {
      HAL_GPIO_WritePin(SPI_PORT,SPI_SDI,GPIO_PIN_RESET); 
      HAL_GPIO_WritePin(SPI_PORT,SPI_SCK,GPIO_PIN_SET);
      HAL_GPIO_WritePin(SPI_PORT,SPI_SCK,GPIO_PIN_RESET);
   }
   for(i=0; i<8; i++)
   {
      if (d & 0x80)
      {
    	  HAL_GPIO_WritePin(SPI_PORT,SPI_SDI,GPIO_PIN_SET); 
      }
      else
      {
    	  HAL_GPIO_WritePin(SPI_PORT,SPI_SDI,GPIO_PIN_RESET);
      }
 
      HAL_GPIO_WritePin(SPI_PORT,SPI_SCK,GPIO_PIN_SET); 
      d <<= 1;   
      HAL_GPIO_WritePin(SPI_PORT,SPI_SCK,GPIO_PIN_RESET); 
   }
}
 
int main(void)
{
  HAL_Init();
  SystemClock_Config();
 
  MX_GPIO_Init();
  _spi_start();
  _spi_sendbyte(64);
  _spi_stop();
 
  while (1);
}
 
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
  __PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
 
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);
 
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1
                              |RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
  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_0);
}
 
void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
 
  /* GPIO Ports Clock Enable */
  __GPIOB_CLK_ENABLE();
 
  /*Configure GPIO pins : PB12 PB13 PB14 PB15 */
  GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

Питание 2.9В, отправленный байт 64, если пересчитать (2.9*64)/256 = 0,725В весьма похоже на то, что показал прибор на выходе.
ad8400_stm32f4disco

В общем таки, если присмотреться то большой разницы нет, поэтому буду потихоньку переползать. Есть мнение что HAL содержит кучу багов, которые регулярно исправляются, поэтому рекомендую почаще обновляться.

Один комментарий: Цифровой резистор AD8400. Немного про HAL

  • Ого на що натрапив. Це ж треба 🙂 Дякую велике. Я вчився по вашим прикладам вісім років тому 😀

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Последние комментарии
  • Загрузка...
Счетчик
Яндекс.Метрика