вторник, 19 сентября 2017 г.

Подключаем Arduino Multifunction Shield к STM32 и управляем семисегментными индикаторами

Для управления семисегментными индикаторами нам понадобятся всего 3 вывода (4, 7, 8):
LATCH_DIO 4 - разрешение записи в регистр (LOW)
CLK_DIO 7 - синхронизация
DATA_DIO 8 - данные (последовательно, побитно)

Данные будут передаваться двумя байтами. Первый - значение индикатора, второй - адрес индикатора. Передача производится слева направо. Индикаторы принимают значения от 0 до 9 - {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}
адрес принимает вид {0xF1,0xF2,0xF4,0xF8}

Принципиальная электрическая схема



Очень похоже на работу SPI не правда ли? Поэтому для отображения цифр на семисегментных индикаторах будем использовать периферийное устройство SPI1 в составе STM32. Ниже представлен код программы который позволяет отобразить на индикаторе цифры 1234.

#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_spi.h"

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    //LATCH_DIO  - разрешение записи в регистр (LOW)
    GPIO_InitTypeDef latch;
    latch.GPIO_Speed = GPIO_Speed_2MHz;
    latch.GPIO_Mode  = GPIO_Mode_Out_PP;
    latch.GPIO_Pin = GPIO_Pin_8 ;
    GPIO_Init(GPIOA, &latch);

    //выводы под SPI CLK_DIO - GPIO_Pin_5, DATA_DIO - GPIO_Pin_7
    GPIO_InitTypeDef gpio;
    gpio.GPIO_Speed = GPIO_Speed_2MHz;
    gpio.GPIO_Mode  = GPIO_Mode_AF_PP;
    gpio.GPIO_Pin   = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_Init(GPIOA, &gpio);

    // настраиваем SPI1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    SPI_InitTypeDef spi;
    spi.SPI_Direction = SPI_Direction_1Line_Tx;
    spi.SPI_Mode = SPI_Mode_Master;
    spi.SPI_DataSize = SPI_DataSize_16b;
    spi.SPI_CPOL = SPI_CPOL_Low; //полярность сигнала синхронизации
    spi.SPI_CPHA = SPI_CPHA_1Edge; //фронт синхронизации
    spi.SPI_NSS = SPI_NSS_Soft;
    spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
    spi.SPI_FirstBit = SPI_FirstBit_MSB; //передавать сначала младший бит
    spi.SPI_CRCPolynomial = 15;
    SPI_Init(SPI1, &spi);
    SPI_Cmd(SPI1, ENABLE);


    while(1)
    {

      while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);// регистр TX пустой?
      SPI_I2S_SendData(SPI1, 0xC0F1);// пишем в TX данные, начало передачи
      while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);// ждём окончания передачи
      GPIO_ResetBits(GPIOA, GPIO_Pin_8);// делаем перепад на LATCH_DIO
      GPIO_SetBits(GPIOA, GPIO_Pin_8);

      while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);// регистр TX пустой?
      SPI_I2S_SendData(SPI1, 0xF9F2);// пишем в TX данные, начало передачи
      while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);// ждём окончания передачи
      GPIO_ResetBits(GPIOA, GPIO_Pin_8);// делаем перепад на LATCH_DIO
      GPIO_SetBits(GPIOA, GPIO_Pin_8);

      while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);// регистр TX пустой?
      SPI_I2S_SendData(SPI1, 0xA4F4);// пишем в TX данные, начало передачи
      while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);// ждём окончания передачи
      GPIO_ResetBits(GPIOA, GPIO_Pin_8);// делаем перепад на LATCH_DIO
      GPIO_SetBits(GPIOA, GPIO_Pin_8);

      while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);// регистр TX пустой?
      SPI_I2S_SendData(SPI1, 0xB0F8);// пишем в TX данные, начало передачи
      while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);// ждём окончания передачи
      GPIO_ResetBits(GPIOA, GPIO_Pin_8);// делаем перепад на LATCH_DIO
      GPIO_SetBits(GPIOA, GPIO_Pin_8);

    }
}



А тут код термометра. Отображает на индикаторах температуру до сотых долей градуса Цельсия.

#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_adc.h"

//значение цифр от 0 до 9
int number[]={0xC000,0xF900,0xA400,0xB000,0x9900,0x9200,0x8200,0xF800,0x8000,0x9000};
//номер индикатора
int indicator[]={0xF1,0xF2,0xF4,0xF8};
int i;
int value;
float temperature = 0.0; //температура в градусах цельсия

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    //LATCH_DIO  - разрешение записи в регистр (LOW)
    GPIO_InitTypeDef latch;
    latch.GPIO_Speed = GPIO_Speed_2MHz;
    latch.GPIO_Mode  = GPIO_Mode_Out_PP;
    latch.GPIO_Pin = GPIO_Pin_8 ;
    GPIO_Init(GPIOA, &latch);

    //выводы под SPI CLK_DIO - GPIO_Pin_5, DATA_DIO - GPIO_Pin_7
    GPIO_InitTypeDef gpio;
    gpio.GPIO_Speed = GPIO_Speed_2MHz;
    gpio.GPIO_Mode  = GPIO_Mode_AF_PP;
    gpio.GPIO_Pin   = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_Init(GPIOA, &gpio);

    // настраиваем SPI1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    SPI_InitTypeDef spi;
    spi.SPI_Direction = SPI_Direction_1Line_Tx;
    spi.SPI_Mode = SPI_Mode_Master;
    spi.SPI_DataSize = SPI_DataSize_16b;
    spi.SPI_CPOL = SPI_CPOL_Low; //полярность сигнала синхронизации
    spi.SPI_CPHA = SPI_CPHA_1Edge; //фронт синхронизации
    spi.SPI_NSS = SPI_NSS_Soft;
    spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
    spi.SPI_FirstBit = SPI_FirstBit_MSB; //передавать сначала младший бит
    spi.SPI_CRCPolynomial = 15;
    SPI_Init(SPI1, &spi);
    SPI_Cmd(SPI1, ENABLE);

    //настраиваем градусник
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    ADC_InitTypeDef term;
    term.ADC_Mode = ADC_Mode_Independent;
    term.ADC_ScanConvMode = DISABLE;
    term.ADC_ContinuousConvMode = ENABLE;
    term.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    term.ADC_DataAlign = ADC_DataAlign_Right;
    term.ADC_NbrOfChannel = 1;

    ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor, 1, ADC_SampleTime_239Cycles5);
    ADC_Init (ADC1, &term);
    ADC_TempSensorVrefintCmd(ENABLE);
    ADC_Cmd (ADC1, ENABLE);

    ADC_ResetCalibration(ADC1);    //Сброс калибровочных данных
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);    //Новая калибровка
    while(ADC_GetCalibrationStatus(ADC1));

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    ADC_Cmd (ADC1, ENABLE);

    while(1)
    {
      for (i=0;i<5000;i++) //чтобы слишком часто не обновлять информацию на индикаторах
      {
          value = temperature/10; //вычисляем значение первого знакоместа
          while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // регистр TX пустой?
          SPI_I2S_SendData(SPI1, number[value]+indicator[0]); // пишем в TX данные, начало передачи
          while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); // ждём окончания передачи
          GPIO_ResetBits(GPIOA, GPIO_Pin_8); // делаем перепад на LATCH_DIO
          GPIO_SetBits(GPIOA, GPIO_Pin_8);

          value = temperature-(value*10); //вычисляем значение второго знакоместа
          while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // регистр TX пустой?
          SPI_I2S_SendData(SPI1, (number[value]+indicator[1])-0x8000); // пишем в TX данные, начало передачи
          while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); // ждём окончания передачи
          GPIO_ResetBits(GPIOA, GPIO_Pin_8); // делаем перепад на LATCH_DIO
          GPIO_SetBits(GPIOA, GPIO_Pin_8);

          value = temperature; //вычисляем значение третьего знакоместа
          value = (temperature*10)-(value*10);
          while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // регистр TX пустой?
          SPI_I2S_SendData(SPI1, number[value]+indicator[2]); // пишем в TX данные, начало передачи
          while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); // ждём окончания передачи
          GPIO_ResetBits(GPIOA, GPIO_Pin_8); // делаем перепад на LATCH_DIO
          GPIO_SetBits(GPIOA, GPIO_Pin_8);

          value = temperature*10; //вычисляем значение четвертого знакоместа
          value = (temperature*100)-(value*10);
          while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // регистр TX пустой?
          SPI_I2S_SendData(SPI1, number[value]+indicator[3]); // пишем в TX данные, начало передачи
          while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); // ждём окончания передачи
          GPIO_ResetBits(GPIOA, GPIO_Pin_8); // делаем перепад на LATCH_DIO
          GPIO_SetBits(GPIOA, GPIO_Pin_8);
      }

      temperature = (1750 - ADC_GetConversionValue(ADC1)) / 4.3 + 25; // результат в градусах Цельсия
    }
}