stm32f103 - Осциллограф

dtvims
Site Admin
Сообщения: 134
Зарегистрирован: Пн авг 02, 2010 2:43 pm

stm32f103 - Осциллограф

Сообщение dtvims » Ср апр 30, 2014 7:44 pm

Начну немного не с темы.

Организация функции Delay(), она же delay_ms() и функции delay_us().
Организация функции задержки через цикл - это некорректно сразу по нескольким причинам. У нас могут использоваться прерывания, которые могут вмешаться в работу функции задержки, причем не раз. Если писать функцию задержки на Си, то ее точность будет сильно зависеть от компилятора. Можно написать на на ассемблере, но проблему с прерываниями это не решит и читабельность кода резко упадет (хотя для кого как).
В любом случае лучше всего использовать таймеры. Таймеры ведут счет не зависимо от того чем занят процессор и прерывания могут внести свою долю в работу такой задержки, но незначительно.

Вариант 1.
инициализируем системный таймер

Код: Выделить всё

RCC_GetClocksFreq(&RCC_Clocks);
SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000);

Создаем для него обработчик прерывания

Код: Выделить всё

void SysTick_Handler(void) // Счетчики милисекунд
{
  if (TimingDelay != 0x00)
  {
     TimingDelay--;
   }
   if (UserTimingDelay != 0x00)
   {
     UserTimingDelay--;
    }
}

Не забываем, конечно, проинициализировать глобальные переменные в самом начале кода

Код: Выделить всё

volatile __IO uint32_t TimingDelay;
volatile __IO uint32_t UserTimingDelay=0;

Эти две переменные - это 2 счетчика, которые декрементируются в обработчике прерывания таймера 1 раз за одну миллисекунду до нуля. Их можно использовать независимо друг от друга, можно еще больше сделать счетчиков, если очень хочется.
Теперь очередь за самой функцией задержки, проще не куда

Код: Выделить всё

void Delay(__IO uint32_t nTime)
{
  TimingDelay = nTime;
  while(TimingDelay != 0);
}

Ошибка данной функции только на время входа выхода в данную функцию и времени выполнения обработчика прерывания, что всего менее чем в одну микросекунду, чем, по сравнению с миллисекундой, можно пренебречь.
Здесь использовалось прерывание, но если мы хотим считать микросекунды, то постоянные вызовы данного прерывания могут внести нежеланные ошибки в ответственную работу основного кода (микрозадержки, но когда их много, уже существенно). Можно обойтись вторым методом.

Вариант 2.
Инициализируем базовый таймер. Но не забываем смотреть в мануалы по конкретному МК, может у Вас вообще нету нужного таймера? На этом потерял кучу времени, думал, что если на серию таймер есть, значит его не может не быть. А вот оказалось, что TIM5 в моем stm32f103c8t6 попросту отсутствует, вернее их там только с TIM1 до TIM4, еще две сторожевые собаки и системный, ВСЕГО 7 штук и ВСЕ! Примеры мне попадались только на TIM5 и я долго не мог понять, что делаю не так и почему таймер не запускается :(. Пока не нашел на китайском форуме недоумение, что почему-то таймеры работают только до 4-го, а остальные не работают. Переделал на 2-й, получил положительный результат и только после этого посмотрел в даташит на камешек...

Код: Выделить всё

/* Enable timer clock - use TIMER2 */
TIM_TimeBaseInitTypeDef Tim2;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
Tim2.TIM_Period=0xFFFF;
Tim2.TIM_Prescaler=(RCC_Clocks.HCLK_Frequency / 1000000)-1;
Tim2.TIM_ClockDivision=TIM_CKD_DIV1;
Tim2.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2,&Tim2);

После данного кода таймер запустится и его счетчик будет крутится по кругу от 0 до 0xFFFF. В функции задержки мы будем включать таймер только когда он нужен, выставлять на старт и ждать пока он досчитает до нужного значения и выключим его - типа ВСЕ!

Код: Выделить всё

void delay_us( uint16_t uSecs )
{
  volatile uint16_t counter=1; // Погрешность в 1мкСекунду
  TIM_Cmd(TIM2,ENABLE);
  TIM_SetCounter(TIM2,counter);
  while(counter<uSecs)
  {
    counter=TIM_GetCounter(TIM2);
  }
  TIM_Cmd(TIM2,DISABLE);
}

В этот раз функция задержки менее идеальная, ведь задержка всего на микросекунды и тут погрешность сравнимая с самой задержкой. Если прикинуть сколько времени уйдет на вызовы всех функций, то получится где-то около одной микросекунды. Вот на одну микросекунду, я внес поправку в счетчик. Тут решений 2: или вносить поправки, или оптимизировать код. Если оптимизировать код, то придется работать напрямую с регистрами и отказаться от включения выключения таймера (честно говоря незнаю нафига его отключаю, вообще нечего идти по стопам чужих примеров), тогда погрешность резко снизится. Работа с регистрами не так привлекательна из-за последующего ухудшения портируемости на другие МК STM32 и читабельности кода, да и мне просто больше нравится использовать стандартные библиотеки, а вот потом, при необходимости будет куда оптимизировать код :)

Ближе к делу.
Поскольку в реализации подобия осциллографа мне понадобилось использование таких задержек, я потратил уйму времени на их организацию и выяснение причин не работающего таймера, решил начать именно с таймера и задержки в микросекундах (вариант для миллисекунд использовал уже и раньше).
А вдохновила меня вот эта тема "http://arduino.ru/forum/proekty/mini-ostsillograf-arduino-na-lcd-5110". Оригинал кода я не искал, но немного его оптимизировал, в том числе для более удобной смены дисплеев, с разным разрешением экрана.

dtvims
Site Admin
Сообщения: 134
Зарегистрирован: Пн авг 02, 2010 2:43 pm

Re: stm32f103 - Осциллограф

Сообщение dtvims » Чт май 01, 2014 11:14 am

Итак берем от STM32 один ADC, в stm32f103 их 2 точно есть, поэтому очень хороший задел на будущее.
Используем 1-й канал, 1-го ADC.
В начале кода выбор управляющих пинов для дисплея, типа D/C или CS, которыми будем управлять в ручную, но SPI будет аппаратный. Дисплей пока от nokia 5110, имеет довольно низкое разрешение 84х48, да еще и медленный, т.е. на максимальную скорость SPI запустить не получится. Инетерсный факт, что на всем семействе STM32 частота SPI делится от 48MHz, а на STM32F10x делится системная частота, т.е. на камешке STM32F103 делится 72MHz, от чего получается очень быстрый SPI - ну опровергнуть или подтвердить не могу, т.к. нет соответствующих приборов для измерения, работает и ладно.

Код: Выделить всё

#include "stm32f10x.h"
#include "font.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define SCK_Pin  GPIO_Pin_5
#define SCK_Pin_Port GPIOA

#define MOSI_Pin GPIO_Pin_7
#define MOSI_Pin_Port GPIOA

#define DC_Pin  GPIO_Pin_7
#define DC_Pin_Port GPIOB

#define RST_Pin GPIO_Pin_5
#define RST_Pin_Port GPIOB

#define SS_Pin  GPIO_Pin_6
#define SS_Pin_Port GPIOB

#define LCDHeight 48
#define LCDWidth 84
#define BuffSize (LCDWidth*LCDHeight/8)

#define swap(a, b) { int16_t t = a; a = b; b = t; }

volatile __IO uint32_t TimingDelay;
volatile __IO uint32_t UserTimingDelay=0;

void Delay(__IO uint32_t nTime)
{
  TimingDelay = nTime;

  while(TimingDelay != 0);
}

/* wait busy loop, microseconds */
void delay_us( uint16_t uSecs )
{
   volatile uint16_t counter=1; // Погрешность в 1мкСекунду
   TIM_Cmd(TIM2,ENABLE);
   TIM_SetCounter(TIM2,counter);
   while(counter<uSecs)
   {
      counter=TIM_GetCounter(TIM2);
   }
   TIM_Cmd(TIM2,DISABLE);
}

void ResetOn() {
    GPIO_SetBits(RST_Pin_Port, RST_Pin);
}

void ResetOff() {
    GPIO_ResetBits(RST_Pin_Port, RST_Pin);
}

void DCOn() {
    GPIO_SetBits(DC_Pin_Port, DC_Pin);
}

void DCOff() {
    GPIO_ResetBits(DC_Pin_Port, DC_Pin);
}

void SSOff() {
    GPIO_ResetBits(SS_Pin_Port, SS_Pin);
}

void SSOn() {
    GPIO_SetBits(SS_Pin_Port, SS_Pin);
}

void SPISend(uint8_t data) {
    SPI_I2S_SendData(SPI1, data);  // отправили данные
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // ждём, пока данные не отправятся
}


uint8_t picture[BuffSize] = // изначально дисплей 102х65, а у меня 84х48, лишнее комментиуем
{
      //0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0xf0, 0xfc,
      0xfa, 0xf6, 0xe6, 0xee, 0xce, 0xde, 0xfe, 0xbf,
      0xbf, 0xff, 0x7f, 0x7f, 0xff, 0xfe, 0xfe, 0xfe,
      0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
      0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf8,
      0xf8, 0xf8, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0,
      0xe0, 0xe0, 0xe0, 0xc0, 0x40, 0x40, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, //0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x00, //0x30,
      0x78, 0xfc, 0xfc, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xe0, 0xc0, 0x8e, 0xbf, 0xbf, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
      0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
      0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
      0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
      0x3c, 0x1e, 0xe, 0x2, 0x0, 0x0, 0x80, 0x80,
      0x80, 0x80, 0x0, //0x0, //0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0,
      0x1, 0x1, 0x3, 0x3, 0x7, 0x7, 0xf, 0xf,
      0xf, 0x1f, 0x1f, 0x1f, 0x1f, 0x3f, 0x3f, 0x3f,
      0x3f, 0x7f, 0x7f, 0x7f, 0x7f, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xee, 0xe4,
      0xf0, 0xf0, 0xf0, 0xf8, 0xff, 0xff, 0xff, 0xff,
      0xff, //0xff, 0xff, 0xff, //0xff, 0xfe, 0xfe, 0xfe,
      //0xfc, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, 0xf0, 0xf0,
      //0xe0, 0xe0, 0xc0,
      0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x2, 0x19, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf,
      0xf, 0xf, 0x1f, 0x1f, 0xdf, 0xff, 0xff, 0xff,
      0xff, 0x7f, 0x7f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
      0x3f, 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0xcf, 0x1f,
      0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, //0x1f,
      //0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xf,
      //0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0x7, 0x3,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70,
      0xf8, 0xfc, 0xfc, 0xfc, 0x3e, 0xe, 0x6, 0x2,
      0x87, 0xff, 0xdf, 0x3f, 0x7f, 0x7f, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0x7, 0x7, 0xf, 0xf, 0xc,
      0xf8, 0xf8, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
      0xe0, 0xf0, 0x30, 0x20, 0x20, 0x80, 0xc0, 0xc0,
      0xc0, 0xc0, 0xc4, 0xcc, 0xcc, 0xcc, 0xd8, 0xf8,
      0xf8, 0xf8, 0x38, 0x8, 0xd, 0xf, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, //0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7,
      0xf, 0x3f, 0x7f, 0xfc, 0xf8, 0xf0, 0xe0, 0xc7,
      0x8f, 0x8f, 0x1e, 0x1c, 0x38, 0x30, 0x31, 0x21,
      0x63, 0x63, 0x47, 0x47, 0x4f, 0x4f, 0x9f, 0x9f,
      0x9f, 0xbf, 0x3f, 0x3f, 0x7f, 0x7f, 0x7f, 0xff,
      0xff, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xff, 0xff,
      0xf0, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0x83, 0x81,
      0x81, 0x80, 0x80, 0x80, 0x0, 0x2, 0x86, 0x86,
      0xc2, 0xc0, 0xc0, 0xfc, 0xfc, 0xfc, 0x74, 0x77,
      0x33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80,
      0xc0, 0x0, 0x0, //0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x3, 0x7, 0x7, 0x7, 0xf, 0xe, 0xe, 0xe, 0xe, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x3c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0xf9, 0xf9, 0x78, 0x38, 0x10, 0x10, 0x13, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x33, 0xbf, 0xfd, 0x69, 0x1, 0x43, 0x43, 0x43, 0xe3, 0xf7, 0x9f, 0x83, 0x1, 0x9, 0x19, 0x10, 0x30, 0x60, 0xe0, 0xc0, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x7, 0x3, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xff, 0x60, 0x30, 0x30, 0x30, 0x18, 0x18, 0x30, 0x18, 0x1c, 0xc, 0xe, 0x7, 0x3, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x3, 0x2, 0x6, 0x6, 0x4, 0xc, 0xf, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};

void LCD_clear() {
   uint16_t i;
   DCOn();
   for(i = 0; i < BuffSize; i++) SPISend(0);
   Delay(1);
   DCOff();
   SPISend(0x00);
}

void LCDInit(void) {

   ResetOff(); //Set LCD reset = 0;
   DCOn(); //Mode = command;
   SSOn(); //Unselect chip;

   //Keep reset pin low for 10 ms
   Delay(10);
   //Release Reset Pin
   ResetOn(); //LCD_RST = 1;
   SSOff();
   DCOff();
   //Configure LCD module
   SPISend(0x21);  //Extended instruction set selected Режим настроек
   SPISend(0xC0);  //Set LCD voltage (defined by experimentation...) Контраст!
   SPISend(0x13);  //Set Bias for 1/48
   //SPISend(0x06);  //Set temperature control (TC2) Необязательно
   SPISend(0x20);  //Revert to standard instruction set
   SPISend(0x0c);  //Set display on in "normal" mode (not inversed) Режим отображения

}

void LCD_set_XY(unsigned char X, unsigned char Y) {
   unsigned char x;
   x = 6 * X;

   DCOff();
   SPISend(0x80 | x);
   SPISend(0x40 | Y);
   Delay(1); // Задержка, чтобы успела примениться последняя команда
   DCOn(); // Принуждает выполнить последнюю команду
}


void LCD_write_char(unsigned char c) {
   unsigned char line;
   unsigned char ch = 0;

   c = c - 32;
   DCOn();

   for (line = 0; line < 6; line++) {
      ch = font6_8[c][line];
      SPISend(ch);

   }
   Delay(1); // Чтобы применился последний байт необходима задержка и пустая команда
   DCOff();  // При использования HW SPI данные действия обязательны, т.к. последний бит должен быть передан при переключении D/C
   SPISend(0x00); // Пустая команда
   Delay(1); // Задержка, чтобы команда была отработана
}

void LCD_write_string(char *s) {
   unsigned char ch;
   while (*s != '\0') {
      ch = *s;
      LCD_write_char(ch);
      s++;
   }
}

void LCD_Write_Dec(unsigned int b) {

   unsigned char datas[3];

   datas[0] = b / 1000;
   b = b - datas[0] * 1000;
   datas[1] = b / 100;
   b = b - datas[1] * 100;
   datas[2] = b / 10;
   b = b - datas[2] * 10;
   datas[3] = b;

   datas[0] += 48;
   datas[1] += 48;
   datas[2] += 48;
   datas[3] += 48;

   LCD_write_char(datas[0]);
   LCD_write_char(datas[1]);
   LCD_write_char(datas[2]);
   LCD_write_char(datas[3]);
}

void LCDisplay(){
    LCD_set_XY(0,0);

//Включаем режим данных и заливаем катинку
    DCOn();
    uint16_t i;
    for(i = 0; i < BuffSize; i++) SPISend(picture[i]);

   Delay(1); // Задержка и пустая команда, чтобы применился последний байт данных
   DCOff();
   SPISend(0x00);
}

void DispClear(){
    uint16_t i;
    for(i = 0; i < BuffSize; i++) picture[i]=0;
}

void DispSetPixel(int16_t x, int16_t y, uint16_t color){
   if(x<0)x=0;
   if(y<0)y=0;
   if(x>=LCDWidth) x = LCDWidth-1;
   if(y>=LCDHeight) y = LCDHeight-1;

   if(color){
     picture[(y/8)*LCDWidth+x] |= 1<<(y%8);
   }else{
     picture[(y/8)*LCDWidth+x] &= ~1<<(y%8);
   }
}

void drawLine(int16_t x0, int16_t y0,
             int16_t x1, int16_t y1,
             uint16_t color) {
  int16_t steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    swap(x0, y0);
    swap(x1, y1);
  }

  if (x0 > x1) {
    swap(x0, x1);
    swap(y0, y1);
  }

  int16_t dx, dy;
  dx = x1 - x0;
  dy = abs(y1 - y0);

  int16_t err = dx / 2;
  int16_t ystep;

  if (y0 < y1) {
    ystep = 1;
  } else {
    ystep = -1;
  }

  for (; x0<=x1; x0++) {
    if (steep) {
       DispSetPixel(y0, x0, color);
    } else {
       DispSetPixel(x0, y0, color);
    }
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}

void interfaceINIT(void){
   // Включаем RCC_APB2Periph_AFIO модуль альтернативной функции, чтобы отключить JTAG и освободить PB4
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_SPI1, ENABLE);
      GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); // Отключаем JTAG

      GPIO_InitTypeDef PORT;
          // выбрали ноги для настройки
      PORT.GPIO_Pin   = SCK_Pin | MOSI_Pin;
          // установили наименьшую скорость (максимальная скорость контроллера 4 Мбита в секунду)
      PORT.GPIO_Speed = GPIO_Speed_50MHz;
          // (важно!) определяем предназначение ног. здесь - выбор "альтернативной функции" ног
      PORT.GPIO_Mode  = GPIO_Mode_AF_PP;
          // настроили ноги в порту А
      GPIO_Init(GPIOA, &PORT);

          // выбрали ноги для настройки
      PORT.GPIO_Pin   = DC_Pin | RST_Pin | SS_Pin | GPIO_Pin_4;
          // установили скорость (тут - без разницы)
      PORT.GPIO_Speed = GPIO_Speed_50MHz;
          // предназначение - общее, выход
      PORT.GPIO_Mode  = GPIO_Mode_Out_PP;
          // настроили ноги в порту B
      GPIO_Init(GPIOB, &PORT);

      SPI_InitTypeDef SPIConf;
          // указываем, что используем мы только передачу данных
      SPIConf.SPI_Direction = SPI_Direction_1Line_Tx;
          // указываем, что наше устройство - Master
      SPIConf.SPI_Mode = SPI_Mode_Master;
          // передавать будем по 8 бит (=1 байт)
      SPIConf.SPI_DataSize = SPI_DataSize_8b;
          // режим 00
      SPIConf.SPI_CPOL = SPI_CPOL_Low;
      SPIConf.SPI_CPHA = SPI_CPHA_1Edge;
      SPIConf.SPI_NSS = SPI_NSS_Soft;
          // установим скорость передачи. Быстрее чем при делителе на 8 не заводится
      SPIConf.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
          // передаём данные старшим битом вперёд (т.е. слева направо)
      SPIConf.SPI_FirstBit = SPI_FirstBit_MSB;
          // внесём настройки в SPI
      SPI_Init(SPI1, &SPIConf);
          // включим  SPI1
      SPI_Cmd(SPI1, ENABLE);
          // SS = 1
      SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);

      ADC_InitTypeDef ADC_InitStructure;
      RCC_ADCCLKConfig(RCC_PCLK2_Div2);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
      ADC_DeInit(ADC1);

      ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;
      ADC_InitStructure.ADC_ScanConvMode=DISABLE;
      ADC_InitStructure.ADC_ScanConvMode=DISABLE;
      ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
      ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
      ADC_InitStructure.ADC_NbrOfChannel=1;
      ADC_Init(ADC1, &ADC_InitStructure);

      ADC_Cmd(ADC1, ENABLE);
      ADC_ResetCalibration(ADC1);
      while(ADC_GetResetCalibrationStatus(ADC1));
      ADC_StartCalibration(ADC1);
      while(ADC_GetCalibrationStatus(ADC1));

      RCC_ClocksTypeDef RCC_Clocks;

      RCC_GetClocksFreq(&RCC_Clocks);
      SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000); //Таймер для функции Delay, срабатывает раз Милисекунду.
        /* Enable timer clock  - use TIMER5 */
      TIM_TimeBaseInitTypeDef Tim2;
      //TIM_DeInit(TIM5);
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
      Tim2.TIM_Period=0xFFFF;
      Tim2.TIM_Prescaler=(RCC_Clocks.HCLK_Frequency / 1000000)-1;
      Tim2.TIM_ClockDivision=TIM_CKD_DIV1;
      Tim2.TIM_CounterMode=TIM_CounterMode_Up;
      TIM_TimeBaseInit(TIM2,&Tim2);
}

uint16_t readADC1(uint8_t channel){
   ADC_RegularChannelConfig(ADC1,channel, 1, ADC_SampleTime_1Cycles5);
   ADC_SoftwareStartConvCmd(ADC1, ENABLE);
   while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)==RESET);
   return ADC_GetConversionValue(ADC1);
}

//Далее осциллограф
// Variables you might want to play with
uint8_t useThreshold = 1;                  // 0 = Off, 1 = Rising, 2 = Falling
uint8_t theThreshold = 80;                // 0-255, Multiplied by voltageConst
unsigned int timePeriod = 200;          // 0-65535, us or ms per measurement (max 0.065s or 65.535s)
uint8_t voltageRange = 1;                  // 1 = 0-3.3V, 2 = 0-1.65V, 3 = 0-0.825V
uint8_t ledBacklight = 100;

uint8_t autoHScale = 1;             // Automatic horizontal (time) scaling
uint8_t linesNotDots = 1;            // Draw lines between data points

// Variables that can probably be left alone
#define numOfSamples 84          // Leave at 100 for 128x64 pixel display
const uint8_t vTextShift = 65;              // Vertical text shift (to vertically align info)
const uint8_t vScale = 39; // 40-1 (0..39)
const uint32_t ADCScale = 4096;
float ScaleConst = 0;
uint32_t HQadcReadings[numOfSamples];
uint8_t adcReadings[numOfSamples];
//uint8_t adcOldReadings[numOfSamples];
uint8_t thresLocation = 0;                 // Threshold bar location
float voltageConst = 0.052381;          // Scaling factor for converting 0-63 to V
float avgV = 0.0;
float maxV = 0.0;
float minV = 0.0;
float ptopV = 0.0;
float theFreq = 0;

void collectData(void) {
  unsigned int tempThres = 0;
  unsigned int i = 0;

  if (autoHScale) {
    // With automatic horizontal (time) scaling enabled,
    // scale quickly if the threshold location is far, then slow down
    if (thresLocation > 5*numOfSamples/8) {
      timePeriod = timePeriod + 10;
    } else if (thresLocation < 3*numOfSamples/8) {
      timePeriod = (timePeriod>10?timePeriod - 10:0);
    } else if (thresLocation > numOfSamples/2+1) {
      timePeriod = timePeriod + 2;
    } else if (thresLocation < numOfSamples/2-1) {
      timePeriod = (timePeriod>2?timePeriod - 2:0);
    }
  }
  // Enforce minimum time periods
 /* if (timePeriod < 10) {
    timePeriod = 10;
  }*/

  // Adjust voltage contstant to fit the voltage range
/*  if (voltageRange == 1) {
    voltageConst = 0.0523810; // 0-3.30V
  } else if (voltageRange == 2) {
    voltageConst = 0.0261905; // 0-1.65V
  } else if (voltageRange == 3) {
    voltageConst = 0.0130952; //0-0.825V
  }*/

  // If using threshold, wait until it has been reached
  if (voltageRange == 1) tempThres = theThreshold * 25; //<<2
  else if (voltageRange == 2) tempThres = theThreshold << 1;
  else if (voltageRange == 3) tempThres = theThreshold;
  if (useThreshold == 1) {
     i = 0; while ((readADC1(ADC_Channel_0)>tempThres) && (i<32768)) i++;
     i = 0; while ((readADC1(ADC_Channel_0)<tempThres) && (i<32768)) i++;
  }
  else if (useThreshold == 2) {
     i = 0; while ((readADC1(ADC_Channel_0)<tempThres) && (i<32768)) i++;
     i = 0; while ((readADC1(ADC_Channel_0)>tempThres) && (i<32768)) i++;
  }

  // Collect ADC readings
  for (i=0; i<numOfSamples; i++) {
    // Takes 35 us with high speed ADC setting
    HQadcReadings[i] = readADC1(ADC_Channel_0);
    if (timePeriod > 0)
       delay_us(timePeriod);
  }
  for (i=0; i<numOfSamples; i++) {
    // Scale the readings to 0-vScale and clip to vScale if they are out of range.
    if (voltageRange == 1) {
      /*if (HQadcReadings[i]>>4 < 0b111111) adcReadings[i] = HQadcReadings[i]>>4 & 0b111111;
      else adcReadings[i] = 0b111111;*/
      adcReadings[i] = HQadcReadings[i]*ScaleConst;
    } else if (voltageRange == 2) {
      /*if (HQadcReadings[i]>>3 < 0b111111) adcReadings[i] = HQadcReadings[i]>>3 & 0b111111;
      else adcReadings[i] = 0b111111;*/
      adcReadings[i] = HQadcReadings[i]*ScaleConst*2;
    } else if (voltageRange == 3) {
      /*if (HQadcReadings[i]>>2 < 0b111111) adcReadings[i] = HQadcReadings[i]>>2 & 0b111111;
      else adcReadings[i] = 0b111111;*/
      adcReadings[i] = HQadcReadings[i]*ScaleConst*4;
    }
    if(adcReadings[i]>vScale){
        adcReadings[i]=vScale;
    }
    // Invert for display
    adcReadings[i] = vScale-adcReadings[i];
  }

  // Calculate and display frequency of signal using zero crossing
  if (useThreshold != 0) {
     if (useThreshold == 1) {
        thresLocation = 1;
        while ((adcReadings[thresLocation]<(vScale -(theThreshold>>2))) && (thresLocation<numOfSamples-1)) (thresLocation++);
        thresLocation++;
        while ((adcReadings[thresLocation]>(vScale -(theThreshold>>2))) && (thresLocation<numOfSamples-1)) (thresLocation++);
     }
     else if (useThreshold == 2) {
        thresLocation = 1;
        while ((adcReadings[thresLocation]>(vScale -(theThreshold>>2))) && (thresLocation<numOfSamples-1)) (thresLocation++);
        thresLocation++;
        while ((adcReadings[thresLocation]<(vScale -(theThreshold>>2))) && (thresLocation<numOfSamples-1)) (thresLocation++);
     }

     theFreq = (float) 1000/(thresLocation * (timePeriod+27)) * 1000;
  }

  // Average Voltage
  avgV = 0;
  for (i=0; i<numOfSamples; i++)
     avgV = avgV + adcReadings[i];
  avgV = (vScale -(avgV / numOfSamples)) * voltageConst;

  // Maximum Voltage
  maxV = vScale;
  for (i=0; i<numOfSamples; i++)
     if (adcReadings[i]<maxV) maxV = adcReadings[i];
  maxV = (vScale-maxV) * voltageConst;

  // Minimum Voltage
  minV = 0;
  for (i=0; i<numOfSamples; i++)
     if (adcReadings[i]>minV) minV = adcReadings[i];
  minV = (vScale-minV) * voltageConst;

  // Peak-to-Peak Voltage
  ptopV = maxV - minV;
}

void OscDisp(void){
   int i,j;
   DispClear();
   if (useThreshold != 0)
       for (i=0; i<127; i+=3){
           DispSetPixel(i,vScale-(theThreshold>>2),1);
           DispSetPixel(i,vScale,1);
       }
   j=LCDWidth/4;
   for (i=0; i<vScale; i+=5) {
      DispSetPixel(0,i,1);
      DispSetPixel(j,i,1);
      DispSetPixel(j+j,i,1);
      DispSetPixel(j+j+j,i,1);
      DispSetPixel(LCDWidth-1,i,1);
   }
   for (i=1; i<numOfSamples-1; i++){ // Draw using lines
      drawLine(i-1,adcReadings[i-1],i,adcReadings[i], 1);
   }
   LCDisplay();
}

int main(void)
{
   SystemInit();

   interfaceINIT();

   LCDInit(); // Инициализация дисплея
   LCDisplay(); // Вывод картинки
   Delay(2000);

    LCD_clear(); // Очистка дисплея
    LCD_set_XY(0,0); // Установка координат для вывода текста
    LCD_write_string(" Core STM32F1 ");
    LCD_write_string("  by DTViMS   ");
    LCD_write_string("Nokia 5110 LCD");
    LCD_write_string("  Oscill 1.0  ");
    LCD_write_string("Откомпилирован");
    LCD_write_string("  Coocox IDE  ");
    Delay(2000);

    ScaleConst=(float)(vScale+1)/(float)ADCScale;
    voltageConst=3.3/(float)(vScale+1);
    char buffer[16];
    while(1)
    {
       collectData();
       OscDisp();
       LCD_set_XY(0,5);
       sprintf(buffer,"%0.2f %0.2f %0.f",maxV,minV,theFreq);
       LCD_write_string(buffer);
       int tmp=(LCDWidth*timePeriod)/1000;
       Delay((tmp<100?100-tmp:0)); // Медленный дисплей, шустрый процессор, надо бы РАВНОМЕРНО тормознуть процесс
    }
}

void SysTick_Handler(void) // Счетчики милисекунд
{
   if (TimingDelay != 0x00)
      {
          TimingDelay--;
      }
   if (UserTimingDelay != 0x00)
      {
         UserTimingDelay--;
      }

}

20140430_150313.jpg

На картинке слева дисплей подключенный к STM32, а справа дисплей цветной, подключенный к arduino, т.е. оригинальный проект данного осциллографа, подредактированный под конкретный монитор.
В варианте STM32 я уже подправил пару недостатков оригинальной программы, но убрал управление по USART. Жду сенсорный мониторчик, чтобы управление сделать на нем.

Продолжение следует.

ВитГо
Сообщения: 8
Зарегистрирован: Вс июн 15, 2014 6:49 am

Re: stm32f103 - Осциллограф

Сообщение ВитГо » Вс июн 15, 2014 7:02 am

извиняюсь за замечание: но вы неправильно отправляете данные по SPI поэтому у вас проблемы с дисплейчиком.. и еще наверняка проекты завязанные на SPI отказываются работать при изменении прескалера SPI...

например, ваша процедура отправки байта по SPI

Код: Выделить всё

void SPISend(uint8_t data) {
    SPI_I2S_SendData(SPI1, data);  // отправили данные
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // ждём, пока данные не отправятся
}

использование флага TXE - таким образом не совсем корректно..

в SPI STM32 организован двойной буфер на отправку,
первый буфер - это простой регистр, как ячейка памяти, в него мы записываем данные на отправку.. он имеет адрес SPI2->DR
из этого регистра данные уходят в сдвиговый регистр (это второй отдельный от первого регистр!!) и уже со сдвигового регистра выдаются на линию..
флаги работают следующим образом:
флаг TXE - сигнализируют о том что данные из регистра SPI2->DR ушли в сдвиговый, и начали передаваться ! - обратите на это внимание - данные которые были в SPI->DR только начали передаваться ! после этого можно записывать новый байт в SPI->DR, но например в случае с дисплеями - нельзя менять уровень линии DC или CS
флаг BSY - сигнализирует о том что данные из сдвигового регистра переданы, и новые данные из регистра SPI->DR загружены небыли (по причине пустоты последнего) - этот флаг и сигнализирует о конце передачи, и именно после сигнала этого флага можно менять уровень линий DC или CS дисплея - не понадобиться никаких искусственных задержек

если вам нужно подождать чтобы данные ушли на дисплей, то процедура должна выглядеть вот так:

Код: Выделить всё

void SPISend(uint8_t data) {
    SPI_I2S_SendData(SPI1, data);  // отправили данные
    while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);
}

в таком виде произойдет выход из процедуры после отправки байта...

и никаких искусственных пауз, задержек в программе больше делать не придется..

у меня есть небольшое описание работе с SPI но ссылку не выкладываю по причине правил вашего форума.

dtvims
Site Admin
Сообщения: 134
Зарегистрирован: Пн авг 02, 2010 2:43 pm

Re: stm32f103 - Осциллограф

Сообщение dtvims » Пт июн 20, 2014 11:27 am

Спасибо за замечание!!!
Пример сам брал от куда-то и не сильно заморачивался, не успеваю на несколько фронтов работать. Стараюсь описывать и делать акцент только на тех вещах, которые найти в сети очень трудно или вообще не возможно.

Не соглашусь лишь с тем, что из-за этого возникли проблемы. Если мы не дождались конца отправки и уже шлем новые (меняем cs или dc) - это говорит о том, что скорость отправки меньше скорости подготовки данных программой, т.е. логично было бы чтобы проблемы появились именно при уменьшении скорости, а при увеличении они должны были исчезнуть. А тут получилось, что именно на маленьких скоростях, до конкретной все работает, а на больших перестает, т.е. упираемся в предел именно дисплея.
А вот флаг я, конечно, себе изменю :)

Про ссылки, я через чур, конечно, заморочился в ограничениях, но никто не запрещает просто текстом оставить :)

ВитГо
Сообщения: 8
Зарегистрирован: Вс июн 15, 2014 6:49 am

Re: stm32f103 - Осциллограф

Сообщение ВитГо » Пт июн 20, 2014 10:32 pm

я пока не упирался в скорость spi при работе с дисплеями.. хотя работаю обычно с stm32f4 с тактом 168 мгц.. - скорость spi обычно 20 мгц...- все дисплеи работают при этой частоте

dtvims
Site Admin
Сообщения: 134
Зарегистрирован: Пн авг 02, 2010 2:43 pm

Re: stm32f103 - Осциллограф

Сообщение dtvims » Сб июн 21, 2014 10:04 am

В f4 делитель скорости spi от частоы 42мГц (какая там штатная частота перифирии), а у f103 делитель от базовой частоты 78мГц, т.о. максимальная частота spi на боле слабом АРМ выше.
А дисплей от 5110 вообще у меня капризный оказался... На f4 сперва на софтварном spi работал, а потом вдруг отказался, но т.к. его важность там была сомнительная, то я его просто отключил.

Upd. А вот цитата из datasheet на дисплей nokia 5110:
Serial interface maximum 4.0Mbits/s

Что приблизительно совпадает с делителем частоты для spi, при котором все работает.


Вернуться в «Микроконтроллеры и автоматизация»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость