
/* ********************************************
   Modul : MAX7219

   Routinen zur Ansteuerung des MAX7219 Segment
   Treibers

   Autor : Christian Julius 12.2015
   ******************************************** */

#include "stm32f4xx.h"
#include "stm32f4xx_spi.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"

/* -------------- Standard Include Files ----------------- */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

/* -------------- Projekt   Include Files ---------------- */
#include "max7219.h"

/* --------------- Special Include Files / Librarys ------ */
/* -------------- Typen Deklarationen--------------------- */
/* -------------- Defintionen ---------------------------- */

// 7 Segment Anzeige an GPIOE
#define SEG7_SPI        SPI1
#define SEG7_CS         GPIO_Pin_5
#define SEG7_CS_HIGH    GPIO_SetBits(GPIOE,SEG7_CS);
#define SEG7_CS_LOW     GPIO_ResetBits(GPIOE,SEG7_CS);


// Hardware Register des Max7219
#define MAX7219_DISPLAY_TEST      0x0F00
#define MAX7219_NORMAL_OP         0x0C01
#define MAX7219_SHUTDOWN          0x0C00
#define MAX7219_INTENSITY         0x0A05        // 0x03 Voreinstellung
#define MAX7219_SCAN_LIMIT        0x0B07        // 0x07 = 8 Digits
#define MAX7219_DECODE_MODE       0x0900
#define MAX7219_DIGIT_7           0x0800
#define MAX7219_DIGIT_6           0x0700
#define MAX7219_DIGIT_5           0x0600
#define MAX7219_DIGIT_4           0x0500
#define MAX7219_DIGIT_3           0x0400
#define MAX7219_DIGIT_2           0x0300
#define MAX7219_DIGIT_1           0x0200
#define MAX7219_DIGIT_0           0x0100
#define MAX7219_DIGIT_BASE        0x0100

/* -------------- Privat Global Variablen ---------------- */
/* -------------- Privat Funktionen Prototypen ----------- */

static void MAX7219_SendWord(uint16_t data);

/* -------------- Funktionen ----------- */

/* Init des 7 Segment Treibers auf SPI 1, zusammen mit dem NRF24L01 */
void  __attribute__((optimize(0))) MAX7219_Init_SPI() {

    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef SPI_InitStruct;

    // PE5 = Chip Select, PA7 = MOSI, PA6 = MISO (nicht verwendet!), PA5 = SCK
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    /*-------- Configuring SCK, MISO, MOSI --------*/
    GPIO_StructInit (&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed  = GPIO_Medium_Speed;
    GPIO_InitStruct.GPIO_OType  = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd   = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* ----- PE5 als CS konfigurieren -------- */
    GPIO_SetBits(GPIOE,SEG7_CS);                         // Glitch vermeiden CS = HIGH (DISABLE)
    GPIO_StructInit (&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin    = SEG7_CS;
    GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Speed  = GPIO_Medium_Speed;
    GPIO_InitStruct.GPIO_OType  = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd   = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOE, &GPIO_InitStruct);

    /* SPI1 die Alternate Pins zuordnen */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);

    /* Die SPI1 einstellen */
    SPI_StructInit(&SPI_InitStruct);
    SPI_InitStruct.SPI_Direction = SPI_Direction_Tx;
    SPI_InitStruct.SPI_Mode      = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize  = SPI_DataSize_16b;
    SPI_InitStruct.SPI_CPOL      = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA      = SPI_CPHA_1Edge;
    SPI_InitStruct.SPI_NSS       = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_Cmd(SPI1, ENABLE);

    return;
}

// Grundeinstellung des Max7219
void MAX7219_Init()
{
    /* Den MAX 7219 adressieren und einstellen */
    MAX7219_SendWord(MAX7219_NORMAL_OP);
    MAX7219_SendWord(MAX7219_SCAN_LIMIT);
    MAX7219_SendWord(MAX7219_INTENSITY);
    MAX7219_ClearAllDigits();
}

/* --------------------------------------------------------------
   Schreibt eine vorzeichenbehaftete 32 Bit Integerzahl
   ins Display.

   Eingabe:  digit: Stelle, (links 7 rechts 0)
             Zahl : -32768 ... +32768
             dp   : Punkt bei Digit n, 0xff =  kein Punkt
  ------------------------------------------------------------- */

void MAX7219_PrintInteger(uint8_t stelle, int32_t zahl, uint8_t dp_stelle)
{
      uint8_t negativ = (zahl < 0);           // Vorzeichen merken

      // zahl = 0? Dann nur 0 eintragen und raus
      if (zahl == 0) {
         MAX7219_SetDigit(stelle,'0',false);
         return;
      }

      /* --- Zerlege die Zahl in BCD Ziffern -----*/
      zahl = abs(zahl);  // Vorzeichen killen
      int32_t rest;
      uint16_t i = 0;
      do {
           rest = zahl % 10;
           zahl /= 10;
           MAX7219_SetDigit(stelle++,'0' + rest,(dp_stelle != i++) ? 0:1);
           if (stelle > 7)
               return;
      } while (zahl > 0);

      // Vorzeichen zuletzt eintragen
      if (negativ)
        MAX7219_SetDigit(stelle,'-',false);


  return;
}


/* ------ Ein 16 Bit Datenwort senden ------- */
static void __attribute__((optimize(0))) MAX7219_SendWord(uint16_t data)
{
    SEG7_CS_LOW;
    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
    SPI_I2S_SendData(SPI1, data);
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY));
    SEG7_CS_HIGH;
}

/* ------ Display löschen ------- */
void MAX7219_ClearAllDigits()
{
    for (uint16_t i = 0; i < 8; i ++)
        MAX7219_SendWord(MAX7219_DIGIT_BASE + (i << 8));
}

/* -----------------------------------------------------------------------------------------------
    Setzt ein Digit, Eingabe = ASCii Zeichen
------------------------------------------------------------------------------------------------- */

/* 0,1,2,3,4,5,6,7,8,9 */
const uint8_t const digit_code[] = {0x7e, 0x30, 0x6d, 0x79, 0x33, 0x5b, 0x5f, 0x70, 0x7f, 0x7b};
/* a,b,c,d,e,f,-,h,H,I,L,N,o,O,P */
const uint8_t const  char_code[] = {0x77, 0x1f, 0x4e, 0x3d, 0x4f, 0x47, 0x01,0x17,0x37,0x06, \
                                    0x0e,0x15,0x1d,0x67,0x1c,0x3e};

void MAX7219_SetDigit(uint8_t digit, uint8_t value, _Bool dp)
{
    uint16_t chr;
    if ((value >= '0') && (value <= '9')) {
        // Es ist eine Zahl zwischen 0 und 9
        uint16_t offs = digit_code[value - '0'];
        // Punkt einmaskieren
        if (dp) offs |= 0x80;
        // Zeichen drucken
        MAX7219_SendWord(MAX7219_DIGIT_BASE + (digit << 8) + offs);

    } else {
        // Es ist ein Sonderzeichen
        uint16_t adr =  MAX7219_DIGIT_BASE + (digit << 8);
        uint16_t offs;
        switch (value) {
            case 'a':
            case 'A':  offs = char_code[0];
                       break;
            case 'b':
            case 'B':  offs = char_code[1];
                       break;
            case 'c':
            case 'C':  offs = char_code[2];
                       break;
            case 'd':
            case 'D':  offs = char_code[3];
                       break;
            case 'e':
            case 'E':  offs = char_code[4];
                       break;
            case 'f':
            case 'F':  offs = char_code[5];
                       break;
            case 'h':  offs = char_code[7];
                       break;
            case 'H':  offs = char_code[8];
                       break;
            case 'i':
            case 'I':  offs = char_code[9];
                       break;
            case 'l':
            case 'L':  offs = char_code[10];
                       break;
            case 'N':
            case 'n':  offs = char_code[11];
                       break;
            case 'o':  offs = char_code[12];
                       break;
            case 'O':  offs = digit_code[0];
                       break;
            case 'p':
            case 'P':  offs = char_code[13];
                       break;
            case 's':
            case 'S':  offs = digit_code[5];
                       break;
            case 'u':  offs = char_code[14];
                       break;
            case 'U':  offs = char_code[15];
                       break;
            case '-':  offs = char_code[6];
                       break;
            default:   offs = char_code[6];
                       break;
        }

        // Punkt nicht vergessen
        if (dp) offs |= 0x80;
        // Eintragen ins Display
        MAX7219_SendWord(adr + offs);
    }

}

