#include "stm32f10x.h"
#include "bmm150.h"
#include <string.h>
#include <stdio.h>

/* =========================================================
   ?????
   ========================================================= */
#define BMM150_ADDR_7BIT   (0x13)     /* ??????:0x13(7-bit) */
#define I2C_USED           I2C2       /* ???? I2C2 */
#define UART_BAUD          115200

/* I2C2: PB10=SCL, PB11=SDA
   USART1: PA9=TX, PA10=RX
*/

/* =========================================================
   ????(???,???)
   ========================================================= */
static void Delay(volatile uint32_t n)
{
    while (n--) __NOP();
}

/* ??? us ??(72MHz ??,??? bmm150 ???) */
static void delay_us_rough(uint32_t us)
{
    while (us--) Delay(8);
}

/* =========================================================
   ?? printf ???(USART1)
   ========================================================= */

/* =========================================================
   GPIO / USART / I2C ???
   ========================================================= */
static void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOF, &GPIO_InitStructure);

    GPIO_ResetBits(GPIOF, GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9);
}

static void USART_Configuration(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    /* PA9 TX ???? */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* PA10 RX ???? */
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate            = UART_BAUD;
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    USART_InitStructure.USART_Parity              = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
}

static void I2C_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef  I2C_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);

    /* PB10 SCL, PB11 SDA: ????(??????/????) */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    I2C_DeInit(I2C2);

    I2C_InitStructure.I2C_Mode                = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle           = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1         = 0x00;
    I2C_InitStructure.I2C_Ack                 = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed          = 100000; /* 100k */

    I2C_Init(I2C2, &I2C_InitStructure);
    I2C_Cmd(I2C2, ENABLE);
}

/* =========================================================
   I2C ??(???)
   ========================================================= */
static int i2c_write_reg(I2C_TypeDef *I2Cx, uint8_t dev7, uint8_t reg, const uint8_t *data, uint32_t len)
{
    uint32_t timeout;

    timeout = 0x3FFFF;
    while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) == SET)
        if (--timeout == 0) return -1;

    I2C_GenerateSTART(I2Cx, ENABLE);
    timeout = 0x3FFFF;
    while (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR)
        if (--timeout == 0) return -2;

    I2C_Send7bitAddress(I2Cx, (uint8_t)(dev7 << 1), I2C_Direction_Transmitter);
    timeout = 0x3FFFF;
    while (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR)
        if (--timeout == 0) { I2C_GenerateSTOP(I2Cx, ENABLE); return -3; }

    I2C_SendData(I2Cx, reg);
    timeout = 0x3FFFF;
    while (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR)
        if (--timeout == 0) { I2C_GenerateSTOP(I2Cx, ENABLE); return -4; }

    while (len--)
    {
        I2C_SendData(I2Cx, *data++);
        timeout = 0x3FFFF;
        while (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR)
            if (--timeout == 0) { I2C_GenerateSTOP(I2Cx, ENABLE); return -5; }
    }

    I2C_GenerateSTOP(I2Cx, ENABLE);
    return 0;
}

static int i2c_read_reg(I2C_TypeDef *I2Cx, uint8_t dev7, uint8_t reg, uint8_t *data, uint32_t len)
{
    uint32_t timeout;

    if (len == 0) return 0;

    timeout = 0x3FFFF;
    while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) == SET)
        if (--timeout == 0) return -1;

    /* START + ?????? */
    I2C_GenerateSTART(I2Cx, ENABLE);
    timeout = 0x3FFFF;
    while (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR)
        if (--timeout == 0) return -2;

    I2C_Send7bitAddress(I2Cx, (uint8_t)(dev7 << 1), I2C_Direction_Transmitter);
    timeout = 0x3FFFF;
    while (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR)
        if (--timeout == 0) { I2C_GenerateSTOP(I2Cx, ENABLE); return -3; }

    I2C_SendData(I2Cx, reg);
    timeout = 0x3FFFF;
    while (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR)
        if (--timeout == 0) { I2C_GenerateSTOP(I2Cx, ENABLE); return -4; }

    /* Re-START + ? */
    I2C_GenerateSTART(I2Cx, ENABLE);
    timeout = 0x3FFFF;
    while (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR)
        if (--timeout == 0) { I2C_GenerateSTOP(I2Cx, ENABLE); return -5; }

    I2C_Send7bitAddress(I2Cx, (uint8_t)(dev7 << 1), I2C_Direction_Receiver);
    timeout = 0x3FFFF;
    while (I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR)
        if (--timeout == 0) { I2C_GenerateSTOP(I2Cx, ENABLE); return -6; }

    if (len == 1)
    {
        I2C_AcknowledgeConfig(I2Cx, DISABLE);
        (void)I2Cx->SR1; (void)I2Cx->SR2;
        I2C_GenerateSTOP(I2Cx, ENABLE);

        timeout = 0x3FFFF;
        while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_RXNE) == RESET)
            if (--timeout == 0) { I2C_AcknowledgeConfig(I2Cx, ENABLE); return -7; }

        *data = I2C_ReceiveData(I2Cx);
        I2C_AcknowledgeConfig(I2Cx, ENABLE);
        return 0;
    }

    while (len)
    {
        if (len == 2)
        {
            I2C_AcknowledgeConfig(I2Cx, DISABLE);
            I2C_GenerateSTOP(I2Cx, ENABLE);
        }

        timeout = 0x3FFFF;
        while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_RXNE) == RESET)
            if (--timeout == 0) { I2C_AcknowledgeConfig(I2Cx, ENABLE); return -8; }

        *data++ = I2C_ReceiveData(I2Cx);
        len--;
    }

    I2C_AcknowledgeConfig(I2Cx, ENABLE);
    return 0;
}

/* =========================================================
   BMM150 ????
   ========================================================= */
static uint8_t g_bmm150_addr7 = BMM150_ADDR_7BIT;

static int8_t bmm150_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr)
{
    uint8_t dev7 = *(uint8_t *)intf_ptr;
    return (i2c_read_reg(I2C_USED, dev7, reg_addr, reg_data, length) == 0) ? BMM150_OK : BMM150_E_COM_FAIL;
}

static int8_t bmm150_i2c_write(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr)
{
    uint8_t dev7 = *(uint8_t *)intf_ptr;
    return (i2c_write_reg(I2C_USED, dev7, reg_addr, reg_data, length) == 0) ? BMM150_OK : BMM150_E_COM_FAIL;
}

static void bmm150_delay_us(uint32_t period_us, void *intf_ptr)
{
    (void)intf_ptr;
    delay_us_rough(period_us);
}

/* =========================================================
   ???
   ========================================================= */
int main(void)
{
    int8_t rslt;
    struct bmm150_dev dev;
    struct bmm150_settings settings;
    struct bmm150_mag_data mag;

    GPIO_Configuration();
    USART_Configuration();
    I2C_Configuration();

    printf("\r\n==============================\r\n");
    printf("STM32 + BMM150 Demo\r\n");
    printf("I2C addr(7-bit)=0x%02X\r\n", g_bmm150_addr7);

    /* ??:??,?? settings ????? */
    memset(&dev, 0, sizeof(dev));
    memset(&settings, 0, sizeof(settings));
    memset(&mag, 0, sizeof(mag));

    dev.intf = BMM150_I2C_INTF;
    dev.intf_ptr = &g_bmm150_addr7;
    dev.read = bmm150_i2c_read;
    dev.write = bmm150_i2c_write;
    dev.delay_us = bmm150_delay_us;

    printf("bmm150_init...\r\n");
    rslt = bmm150_init(&dev);
    printf("bmm150_init ret=%d\r\n", rslt);
    if (rslt != BMM150_OK)
    {
        printf("init failed, check wiring/pullup/I2C port\r\n");
        while (1)
        {
            GPIO_SetBits(GPIOF, GPIO_Pin_6);
            Delay(0x5FFFFF);
            GPIO_ResetBits(GPIOF, GPIO_Pin_6);
            Delay(0x5FFFFF);
        }
    }

    /* ??:??? DRDY,?????? */
    settings.pwr_mode = BMM150_POWERMODE_NORMAL;
    rslt = bmm150_set_op_mode(&settings, &dev);
    printf("set_op_mode ret=%d\r\n", rslt);

    settings.preset_mode = BMM150_PRESETMODE_REGULAR;
    rslt = bmm150_set_presetmode(&settings, &dev);
    printf("set_presetmode ret=%d\r\n", rslt);

    printf("Start read mag...\r\n");

    while (1)
    {
        rslt = bmm150_read_mag_data(&mag, &dev);
        if (rslt == BMM150_OK)
        {
            printf("X:%d  Y:%d  Z:%d\r\n", mag.x, mag.y, mag.z);
            GPIO_SetBits(GPIOF, GPIO_Pin_7);
        }
        else
        {
            printf("read err=%d\r\n", rslt);
            GPIO_ResetBits(GPIOF, GPIO_Pin_7);
        }

        Delay(0x5FFFFF);
    }
}
