반응형

CubeMX 설치 후, New Project를 선택하면 아래와 같은 화면이 나타난다.
Commercial Part Number에 "STM32F722RET6"를 입력하면 해당 items이 나타난다.
STM32F722RET6를 선택한 후, 상단 우측의 Start Project를 선택한다.
이어서 MPU 관련 Pop-up이 나타나는데, 나는 사용하지 않을테니 No를 선택한다.
이어서 New Project를 생성하기 위해 로딩 진행 중...
STM32F722RETx MCU의 Pin Map Structure가 나타난다.
다음과 같은 회로도를 기준으로 본격적으로 Pin Map을 설정하기로 하자.
가장 먼저 회로도의 PB8에 해당하는 I2C1_SCL를 설정한다. 나머지도 이와같이 선택하고 설정하면 된다.
I2C 및 UART, USB가 아닌 GPIO의 Output Control를 위한 선택은 위와 같이 하면 된다.
회로도 상의 필요한 기능에 대해 모두 할당한 상태이다.
현재까지 선택한 기능들의 System View이다.
System Core의 GPIO 관련 옵션 설정 중에 Maximum output speed를 Very High로 설정해주면, 216Mhz의 clock speed 장점을 누릴 수가 있다.
System Core의 RCC 옵션에서 HSE/LSE 옵션 설정을 한다.
System Core의 SYS 옵션은 System Wake-Up 1 옵션을 선택한다.
Connectivity의 I2C 옵션을 선택하는 곳인데 I2C Speed Mode를 Fast Mode로 설정하여 400khz를 사용하도록 하자. 나머지 I2C2 채널도 마찬가지이다.
NVIC Setting에서 NVIC interrupt Table도 Enable를 선택하자.
I2C의 GPIO Pull-up/Pull-down의 옵션 설정이 안되고 있는데, IDE code 상에서 Pull-up 옵션으로 설정해주어야 원활한 I2C 정상동작이 된다.
Connectivity의 UART1 옵션을 선택한 화면이다.
Connectivity의 USB_OTG_FS의 "Device_Only"를 선택해야 Virtual COM port를 사용할 수가 있다.
Middleware의 USB_DEVICE에서 Commnunication Device Class (Virtual Port COM) 선택 후, buffer size를 64Bytes로 변경한다.
화면 상단의 Clock Configuration을 선택하면 나타나며, 모두 Check 후, Yes를 누른다.
HCLK (Mhz) 입력칸에 216을 입력하고 Enter Key를 누르면 MCU 각종 Bus에 대해 자동적으로 주파서 설정이 진행된다.
일정 시간이 지나면, 모든 bus에 대해 주파수 설정이 자동적으로 변경된 것을 확인할 수가 있다.
Toolchain / IDE에서 STM32CubeIDE를 선택한 후에 Generate Code를 선택하면 자동적으로 CubeIDE source code가 생성된다.
기본 설정을 그대로 사용한다.
지금까지 선택한 옵션들에 대한 정보이다. (생략)

 

반응형
반응형

STM32F103 series MCU의 hardware SPI interface를 사용하기 위해서 아래와 같이 GPIO 설정이 필요로 한다.

 

STM32F103RBT6 datasheet

#define GPIO_HW_SPI            GPIOA
#define GPIO_HW_SPI_CS                  GPIO_Pin_4    // NSS PIN
#define GPIO_HW_SPI_SCLK                GPIO_Pin_5    // Clock
#define GPIO_HW_SPI_MISO                GPIO_Pin_6    // Master Input to Slave Output
#define GPIO_HW_SPI_MOSI                GPIO_Pin_7    // Master Output to Slave Input

#define HW_SPI_READ_BIT			0x80
#define HW_SPI_WRITE_BIT		0x00

/* Select hardware SPI: Chip Select pin low  */
#define HW_SPI_CS_LOW()       GPIO_ResetBits(GPIO_HW_SPI, GPIO_HW_SPI_CS)
/* Deselect hardware SPI: Chip Select pin high */
#define HW_SPI_CS_HIGH()      GPIO_SetBits(GPIO_HW_SPI, GPIO_HW_SPI_CS)

void MXM1160_HW_SPI_GPIO_Init(void){

    SPI_InitTypeDef  SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    /* Enable SPI1 and GPIO clocks */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

    /* Configure SPI1 pins: SCK, MISO and MOSI */
    GPIO_InitStructure.GPIO_Pin   =  GPIO_HW_SPI_SCLK | GPIO_HW_SPI_MOSI | GPIO_HW_SPI_MISO;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIO_HW_SPI, &GPIO_InitStructure);

    /* Configure I/O for the sensor select */
    GPIO_InitStructure.GPIO_Pin   =  GPIO_HW_SPI_CS;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIO_HW_SPI, &GPIO_InitStructure);

    /* Deselect the hardware SPI : Sensor Select high */
    HW_SPI_CS_HIGH();

    /* SPI1 configuration */
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 1;
    SPI_Init(SPI1, &SPI_InitStructure);

    /* Enable SPI1  */
    SPI_Cmd(SPI1, ENABLE);    
}

 

위의 'SPI_InitStructure.SPI_CPOL"와 'SPI_InitStructure.SPI_CPHA'는 사용자 개발 환경과 SPI 통신을 사용하는 slave chip의 통신 모드의 Clock Plarity (CPOL), Clock Phase (CPHA)의 의해 결정되므로 개별적으로 확인하길 바란다.

 

그 다음에 SPI 통신은 양방향 통신이기 때문에 Read / Write command에 따라 dummy byte 등 적절한 array index 관리를 해주면 된다.

 

void  HW_SPI_Multi_TXRX(uint8_t Cmd, uint8_t addr, uint8_t count, uint8_t  *pData)
{
    uint8_t BufferSize = count;
    uint8_t SPI_Dummy_Tx = 0x00;
    uint8_t SPI_Dummy_Rx = 0x00;
	
    // Check the Command mode and Address
    SPI_Dummy_Tx = Cmd | addr;

    HW_SPI_CS_LOW();

    /* Wait for SPIy Tx buffer empty */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    /* Send SPIy data */
    SPI_I2S_SendData(SPI1, SPI_Dummy_Tx);

    /* Wait for SPIy data reception */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    /* Read SPIy received data */
    SPI_Dummy_Rx = SPI_I2S_ReceiveData(SPI1);

    while(count>1){

        /* Wait for SPIy Tx buffer empty */
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
        /* Send SPIy data */
        SPI_I2S_SendData(SPI1, pData[BufferSize-count]);

        /* Wait for SPIy data reception */
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
        /* Read SPIy received data */
        if ( Cmd == HW_SPI_READ_BIT ){
            pData[BufferSize-count] = SPI_I2S_ReceiveData(SPI1);
        }
        else{
            SPI_Dummy_Rx = SPI_I2S_ReceiveData(SPI1);
        }			
        count--;
    }

    /* Wait for SPIy Tx buffer empty */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    /* Send SPIy data */
    SPI_I2S_SendData(SPI1, pData[BufferSize-count]);

    /* Wait for SPIy data reception */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    /* Read SPIy received data */
    if ( Cmd == HW_SPI_READ_BIT ){
        pData[BufferSize-count] = SPI_I2S_ReceiveData(SPI1);
    }
    else{
        SPI_Dummy_Rx = SPI_I2S_ReceiveData(SPI1);
    }
		
    HW_SPI_CS_HIGH();
}

 

마지막으로 SPI 통신의 waveform grpah 관련 자료는 구글링을 하면 되므로, 검색 스킬을 발휘하길 바란다.

 

끝.

반응형
반응형

본인은 STM32F103 시리즈 MCU를 사용하고 있으며, Interrupt 기능을 사용하기 위해 GPIOA Pin 4번을 이용하여 인터럽트 관련 설정 코드를 아래와 같이 하였다.

 

void STM32_Interrupt_initial(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_AFIO, ENABLE);

	// GPIOA Pin 4 Interrupt
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	// Connect EXTI Line to gpio pin
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource4);

	// Configure EXTI Line to generate an interrupt
	EXTI_InitStructure.EXTI_Line    = EXTI_Line4;
	EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);

	// Configure the NVIC Preemption Priority Bits
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

	// Enable the EXTI4 Interrupt
	NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

EXTI 및 NVIC 관련 설정은 구글링을 하면 많은 자료가 검색 되므로, 굳이 설명을 하지 않기로 한다.

참고로 "EXTI1_IRQHandler" ~ "EXTI4_IRQHandler", "EXTI9_5_IRQHandler", "EXTI15_10_IRQHandler"의 IRQ 핸들러를 설정할 수 있는데, Pin0 부터 Pin4까지는 지정된 IRQHandler를 사용해야 하고, Pin5 부터 Pin9는 EXTI9_5_IRQHandler를 사용해야 한다. 마찬가지로 Pin10 부터 Pin15는 EXTI15_10_IRQHandler를사용해야한다.

 

/*******************************************************************************
* Function Name  : EXTI4_IRQHandler
* Description    : This function handles External interrupt Line 4 request.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void EXTI4_IRQHandler(void)
{
	if(EXTI_GetITStatus(GPIO_Pin_4) != RESET)
	{
		printf("Interrupt OK\n");

		// Clear the Interrupt EXTI line pending bit
		EXTI_ClearITPendingBit(GPIO_Pin_4);		
	}
}

이렇게 하여, 해당 GPIO Pin에 Failling Signal을 전달하면, 아래와 같이 debug message가 나타난다.

 

 

반응형
반응형

STM32F103 series로 maximum 72Mhz system clock를 사용하려면 외부 12Mhz oscillator를 실장하여 HSE (High Speed External) crystal output clock frequency mode로 PLL multiplication factor 제어하여 6배 (PLL input clock x 6) 곱하여 72Mhz PLLCLK source로 사용하고 있었다.

 

그러나 external crystal 사용에 따른 부품 단가로 인하여 비용 절감(?)을 위해 internal 8Mhz RC Oscillator를 사용하여 HSI (High Speed Internal) crystal output clock frequency mode에서 아래 그림을 참고하면 2 분주 후, 16배 곱하여 maximum 64Mhz system clock를 사용할 수가 있다. 

 

(8Mhz / 2) x 16 = 64Mhz

 

그리고 internal 8Mhz RC Oscillator에 따른 HSI crystral output clock frequency mode의 장단점은 아래와 같다. 단지 main system clock frequency의 accuracy가 저하된다고 하는데, 과연 MCU 시스템에 큰 영향을 끼칠까?

 

The HSI RC oscillator has the advantage of providing a clock source at low cost (no external components). It also has a faster startup time than the HSE crystal oscillator however, even with calibration the frequency is less accurate than an external crystal oscillator or ceramicresonator.

 

참고로 STM32F103은 다음과 같이 3가지 종류의 clock source를 제공하고 있다.

 

- HSI Oscillator clock

- HSE Oscillator clock

- PLL clock

 

그리고 다음과 같이 maximum clock frequeny가 정의되어 있으므로, 적절하게 선정하여 사용 하도록 하자.

 

The maximum frequency of the AHB and the APB2 domains is 72 MHz. The maximum allowed frequency of the APB1 domain is 36 MHz.

 

위의 내용을 토대로 HSI mode를 사용하여 main 64Mhz system clock frequency를 출력하는 예제 코드는 아래와 같다.

 

void RCC_Configuration_HSI_64Mhz_without_USBclock(void)
{
    ErrorStatus HSIStartUpStatus;

    /* RCC system reset(for debug purpose) */
    RCC_DeInit();

    /* activate HSI (Internal High Speed oscillator) */
    RCC_HSICmd(ENABLE);

    /* Wait until RCC_FLAG_HSIRDY is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);

    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    /* Flash 2 wait state */
    FLASH_SetLatency(FLASH_Latency_2);

    /* HCLK = SYSCLK */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);// HCLK = 64 MHz, AHB

    /* PCLK1 = HCLK/2 */
    RCC_PCLK1Config(RCC_HCLK_Div2); // APB1 = 32 MHz

    /* PCLK2 = HCLK */
    RCC_PCLK2Config(RCC_HCLK_Div1); // APB2 = 64 MHz

    /* PLLCLK = (8MHz/2) * 16 = 64MHz */
    RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_16); // 64 MHz

    /* Enable PLL */ 
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while(RCC_GetSYSCLKSource() != 0x08);
 
}

 

그러나 위의 과정을 거쳐서 USB clock를 사용하게 되면 문제가 발생되는데, 다음과 같이 USB clock를 사용하려면 48Mhz clock이 필요로하다. 그래서 PLL clock은 72Mhz 및 48Mhz의 output clock frequency가 필요하다.

 

The USB OTG FS 48 MHz clock which is derived from the PLL VCO clock (2 × PLLCLK), followed by a programmable prescaler (divide by 3 or 2). 

This selection is  made through the OTGFSPRE bit in the RCC_CFGR register. 

For proper USB OTG FSoperation, the PLL should be configured to output 72 MHz or 48 MHz.

 

위의 내용을 토대로 HSI mode를 사용하여 main 48Mhz system clock frequency로 설정하고 48Mhz USB clock 출력하는 예제 코드는 아래와 같다.

 

void RCC_Configuration_HSI_48Mhz_with_USBclock(void)
{
    ErrorStatus HSIStartUpStatus;

    /* RCC system reset(for debug purpose) */
    RCC_DeInit();

    /* activate HSI (Internal High Speed oscillator) */
    RCC_HSICmd(ENABLE);

    /* Wait until RCC_FLAG_HSIRDY is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);

    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    /* Flash 2 wait state */
    FLASH_SetLatency(FLASH_Latency_2);

    /* HCLK = SYSCLK */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);// HCLK = 48 MHz, AHB

    /* PCLK1 = HCLK/2 */
    RCC_PCLK1Config(RCC_HCLK_Div2); // APB1 = 24 MHz

    /* PCLK2 = HCLK */
    RCC_PCLK2Config(RCC_HCLK_Div1); // APB2 = 48 MHz

    /* PLLCLK = (8MHz/2) * 12 = 48MHz */
    RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_12); // 48 MHz

    /* Enable PLL */ 
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while(RCC_GetSYSCLKSource() != 0x08);
 
}

 

참고로 USB clock 관련 설정은 아래와 같이 code 분리하여 프로그래밍을 하여도 된다.

 

/******************************************************************************* 
* Function Name  : Set_USBClock 
* Description    : Configures USB Clock input (48MHz) 
* Input          : None. 
* Return         : None. 
*******************************************************************************/ 
void Set_USBClock(void) 
{ 
    // Select USBCLK source 
    RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div1); 

    // Enable the USB clock 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE); 
}

/*******************************************************************************
* Function Name  : USB_Interrupts_Config
* Description    : Configures the USB interrupts
* Input          : None.
* Return         : None.
*******************************************************************************/
void USB_Interrupts_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    // 2 bit for pre-emption priority, 2 bits for subpriority
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    // Enable the USB interrupt
    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // Enable the USB Wake-up interrupt
    NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // Enable the DMA1 Channel1 Interrupt
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/********************** main.c ***********************/
Set_USBClock();
USB_Interrupts_Config();
USB_Init(); // STM32 USB library at 'usb_init.c'

 

[부록]

System clock frequency를 48Mhz로 설정하고 나니, 문득 드는 생각이 있는데 GPIO_Speed_2MHz, GPIO_Speed_10MHz, GPIO_Speed_50MHz 의 설정값들이 있는데 GPIO Speed 50Mhz 설정에 따른 GPIO speed 관련 내용을 수집하여 보니, 해당 GPIO speed는 system clock과 관계없이 ARM core의 bus line speed에 대한 의미로 이해하면 된다. 어차피 아래 STM32F103 datasheet 내용을 참조하면 maximum 72Mhz system clock frequency 설정에 따른 maximum GPIO toggling speed는 18Mhz 이다. (GPIO speed 2Mhz의 장점은 EMI noise가 작아진다. 거꾸로 GPIO speed 50Mhz로 설정하면 EMI noise가 높아질 확률이다.)

 

GPIOs (general-purpose inputs/outputs)
Each of the GPIO pins can be configured by software as output (push-pull or open-drain), as input (with or without pull-up or pull-down) or as peripheral alternate function. Most of the GPIO pins are shared with digital or analog alternate functions. All GPIOs are high currentcapable. The I/Os alternate function configuration can be locked if needed following a specific sequence in order to avoid spurious writing to the I/Os registers. I/Os on APB2 with up to 18 MHz toggling speed. 

 

마지막으로 혹시 몰라서 HSE (High Speed External) crystal output clock frequency mode 관련 예제 코드도 공유한다.

 

void RCC_Configuration_HSE(void)
{
    ErrorStatus HSEStartUpStatus;

    /* RCC system reset(for debug purpose) */
    RCC_DeInit();

    /* Enable HSE */
    RCC_HSEConfig(RCC_HSE_ON);

    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();

    if(HSEStartUpStatus == SUCCESS)
    {
        /* Enable Prefetch Buffer */
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

        /* Flash 2 wait state */
        FLASH_SetLatency(FLASH_Latency_2);

        /* HCLK = SYSCLK */
        RCC_HCLKConfig(RCC_SYSCLK_Div1); 

        /* PCLK1 = HCLK/2 */
        RCC_PCLK1Config(RCC_HCLK_Div2);

        /* PCLK2 = HCLK */
        RCC_PCLK2Config(RCC_HCLK_Div1); 

        /* ADCCLK = PCLK2/4 */
        RCC_ADCCLKConfig(RCC_PCLK2_Div6); 

        /* PLLCLK = 12MHz * 6 = 72 MHz */
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_6);

        /* Enable PLL */ 
        RCC_PLLCmd(ENABLE);

        /* Wait till PLL is ready */
        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

        /* Select PLL as system clock source */
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

        /* Wait till PLL is used as system clock source */
        while(RCC_GetSYSCLKSource() != 0x08);
    }    
}

 

반응형
반응형

STM32F10x series MCU를 사용하면서 제공되는 stm32f10x_i2c.h / stm32f10x_i2c.c 관련 STM32 library를 사용한 hardware I2C example code를 공유한다.

 

# i2c_hw.h

#ifndef __I2C_HW_H
#define __I2C_HW_H

/* includes */
#include "stm32f10x.h"
#include "hw_config.h"
/* defines */
/* functions */
void HW_I2C_master_initial(void);

uint8_t HW_I2C_Read(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr, uint16_t NumByteToRead, uint8_t* pBuffer);
uint8_t HW_I2C_Write(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr, uint16_t NumByteToWrite, uint8_t* pBuffer);
uint8_t I2C_Check_SlaveAddr(I2C_TypeDef* I2Cx, uint8_t DeviceAddr);

uint8_t HW_I2C_UTIL_Write(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr, uint8_t pBuffer);
uint8_t HW_I2C_UTIL_Read(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr);
void HW_I2C_UTIL_Write_Multi(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr, uint16_t NumByteToWrite, uint8_t* pBuffer);
uint8_t HW_I2C_UTIL_Read_Multi(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr, uint8_t ReadCount, uint8_t (*pReadData));
uint8_t HW_I2C_UTIL_Check_SlaveAddr(I2C_TypeDef* I2Cx);

#endif  /* __I2C_HW_H */

 

# i2c_hw.c

#include "i2c_hw.h"

#define MAX_COMMUNICATION_FREQ   ((uint32_t) 400000)	// 100khz
#define FLAG_TIMEOUT             ((uint32_t)0x1000)
#define LONG_TIMEOUT             ((uint32_t)(10 * FLAG_TIMEOUT))

#define GPIO_I2C_HW        GPIOB

#define GPIO_I2C1_SCL	GPIO_Pin_6
#define GPIO_I2C1_SDA	GPIO_Pin_7
#define GPIO_I2C2_SCL	GPIO_Pin_10
#define GPIO_I2C2_SDA	GPIO_Pin_11

__IO uint32_t  UTIL_Timeout = LONG_TIMEOUT;
__IO uint16_t  UTIL_Address = 0;

void HW_I2C_master_initial(void)
{
	I2C_InitTypeDef 	I2C_InitStructure;
	GPIO_InitTypeDef	GPIO_InitStructure;

	// I2C_SCL_GPIO_CLK and I2C_SDA_GPIO_CLK Periph clock enable
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	// I2C Periph clock enable
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);

	// Configure I2C pins: SCL , SDA
	GPIO_InitStructure.GPIO_Pin = GPIO_I2C1_SCL | GPIO_I2C1_SDA | GPIO_I2C2_SCL | GPIO_I2C2_SDA;;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_Init(GPIO_I2C_HW, &GPIO_InitStructure);

	// Free all used resources
	I2C_DeInit(I2C1);
	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 = MAX_COMMUNICATION_FREQ;
	I2C_Init(I2C1, &I2C_InitStructure);
	I2C_Init(I2C2, &I2C_InitStructure);

	I2C_Cmd(I2C1, ENABLE);
	I2C_Cmd(I2C2, ENABLE);
}

uint8_t HW_I2C_Read(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr, uint16_t NumByteToRead, uint8_t* pBuffer)
{
  __IO uint32_t UTIL_Timeout = LONG_TIMEOUT;
  __IO uint32_t temp;

  (void)(temp);

restart:

  UTIL_Timeout = LONG_TIMEOUT;
/* Send START condition */
  I2C_GenerateSTART(I2Cx, ENABLE);
  /* Test on EV5 and clear it */
  while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if (UTIL_Timeout-- == 0)
      return ERROR;
  }

  UTIL_Timeout = LONG_TIMEOUT;
  /* Send slave address for read */
  I2C_Send7bitAddress(I2Cx, DeviceAddr, I2C_Direction_Transmitter);

  while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    if (UTIL_Timeout-- == 0)
    {
      I2C_ClearFlag(I2Cx,I2C_FLAG_BUSY|I2C_FLAG_AF);
      goto restart;
    }
  }
  /* Clear EV6 by setting again the PE bit */
  I2C_Cmd(I2Cx, ENABLE);

  I2C_SendData(I2Cx, RegisterAddr);

  /* Test on EV8 and clear it */
  UTIL_Timeout = LONG_TIMEOUT;
  while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    if (UTIL_Timeout-- == 0)
     return ERROR;
  }

  if (NumByteToRead == 0x01)
  {
    restart3:
    /* Send START condition */
    I2C_GenerateSTART(I2Cx, ENABLE);
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
    /* Send Slave address for read */
    I2C_Send7bitAddress(I2Cx, DeviceAddr, I2C_Direction_Receiver);
    /* Wait until ADDR is set */
    UTIL_Timeout = LONG_TIMEOUT;
    while (!I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR))
    {
      if (UTIL_Timeout-- == 0)
      {
        I2C_ClearFlag(I2Cx,I2C_FLAG_BUSY|I2C_FLAG_AF);
        goto restart3;
      }
    }
    /* Clear ACK */
    I2C_AcknowledgeConfig(I2Cx, DISABLE);
    __disable_irq();
    /* Clear ADDR flag */
    temp = I2Cx->SR2;
    /* Program the STOP */
    I2C_GenerateSTOP(I2Cx, ENABLE);
    __enable_irq();
    while ((I2C_GetLastEvent(I2Cx) & 0x0040) != 0x000040); /* Poll on RxNE */
    /* Read the data */
    *pBuffer = I2C_ReceiveData(I2Cx);
    /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
    while ((I2Cx->CR1&0x200) == 0x200);
    /* Enable Acknowledgement to be ready for another reception */
    I2C_AcknowledgeConfig(I2Cx, ENABLE);

    return SUCCESS;
  }
  else
    if(NumByteToRead == 0x02)
    {
      restart4:
      /* Send START condition */
      I2C_GenerateSTART(I2Cx, ENABLE);
      while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
      /* Send EEPROM address for read */
      I2C_Send7bitAddress(I2Cx, DeviceAddr, I2C_Direction_Receiver);
      I2Cx->CR1 = 0xC01; /* ACK=1; POS =1 */
      UTIL_Timeout = LONG_TIMEOUT;
      while (!I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR))
      {
        if (UTIL_Timeout-- == 0)
        {
          I2C_ClearFlag(I2Cx,I2C_FLAG_BUSY|I2C_FLAG_AF);
          goto restart4;
        }
      }
      __disable_irq();
      /* Clear ADDR */
      temp = I2Cx->SR2;
      /* Disable ACK */
      I2C_AcknowledgeConfig(I2Cx, DISABLE);
      __enable_irq();
      while ((I2C_GetLastEvent(I2Cx) & 0x0004) != 0x00004); /* Poll on BTF */

       __disable_irq();
      /* Program the STOP */
      I2C_GenerateSTOP(I2Cx, ENABLE);
      /* Read first data */
      *pBuffer = I2Cx->DR;
      pBuffer++;
      /* Read second data */
      *pBuffer = I2Cx->DR;
      __enable_irq();
      I2Cx->CR1 = 0x0401; /* POS = 0, ACK = 1, PE = 1 */

      return SUCCESS;
    }
  else
  {
restart2:
    UTIL_Timeout = LONG_TIMEOUT;
    /* Send START condition */
    I2C_GenerateSTART(I2Cx, ENABLE);
    /* Test on EV5 and clear it */
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
    {
      if (UTIL_Timeout-- == 0) return ERROR;
    }
    UTIL_Timeout = LONG_TIMEOUT;
    /* Send slave address for read */
    I2C_Send7bitAddress(I2Cx,  DeviceAddr, I2C_Direction_Receiver);
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {

      if (UTIL_Timeout-- == 0)
      {
        I2C_ClearFlag(I2Cx,I2C_FLAG_BUSY|I2C_FLAG_AF);
        goto restart2;
      }
    }

    /* While there is data to be read; here the safe procedure is implemented */
    while (NumByteToRead)
    {

      if (NumByteToRead != 3) /* Receive bytes from first byte until byte N-3 */
      {
        while ((I2C_GetLastEvent(I2Cx) & 0x00004) != 0x000004); /* Poll on BTF */
        /* Read data */
        *pBuffer = I2C_ReceiveData(I2Cx);
        pBuffer++;
        /* Decrement the read bytes counter */
        NumByteToRead--;
      }

      if (NumByteToRead == 3)  /* it remains to read three data: data N-2, data N-1, Data N */
      {

        /* Data N-2 in DR and data N -1 in shift register */
        while ((I2C_GetLastEvent(I2Cx) & 0x000004) != 0x0000004); /* Poll on BTF */
        /* Clear ACK */
        I2C_AcknowledgeConfig(I2Cx, DISABLE);
        __disable_irq();
        /* Read Data N-2 */
        *pBuffer = I2C_ReceiveData(I2Cx);
        pBuffer++;
        /* Program the STOP */
        I2C_GenerateSTOP(I2Cx, ENABLE);
        /* Read DataN-1 */
        *pBuffer = I2C_ReceiveData(I2Cx);
        __enable_irq();
        pBuffer++;
        while ((I2C_GetLastEvent(I2Cx) & 0x00000040) != 0x0000040); /* Poll on RxNE */
        /* Read DataN */
        *pBuffer = I2Cx->DR;
        /* Reset the number of bytes to be read by master */
        NumByteToRead = 0;
      }
    }
    /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
    while ((I2Cx->CR1&0x200) == 0x200);
    /* Enable Acknowledgement to be ready for another reception */
    I2C_AcknowledgeConfig(I2Cx, ENABLE);

    return SUCCESS;
  }
}

uint8_t HW_I2C_Write(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr,
                               uint16_t NumByteToWrite,
                               uint8_t* pBuffer)
{

 restart1:
  UTIL_Timeout = LONG_TIMEOUT;
  /* Send START condition */
  I2C_GenerateSTART(I2Cx, ENABLE);
  /* Test on EV5 and clear it */
  while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if (UTIL_Timeout-- == 0) return ERROR;
  }
  /* Send slave address for write */
  I2C_Send7bitAddress(I2Cx, DeviceAddr, I2C_Direction_Transmitter);

  UTIL_Timeout = LONG_TIMEOUT;
  /* Test on EV6 and clear it */
  while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {

    if (UTIL_Timeout-- == 0)
    {
      I2C_ClearFlag(I2Cx,I2C_FLAG_BUSY|I2C_FLAG_AF);
      goto restart1;
    }
  }

  UTIL_Timeout = LONG_TIMEOUT;

  /* Transmit the first address for r/w operations */
  I2C_SendData(I2Cx, RegisterAddr);

  /* Test on EV8 and clear it */
  while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    if (UTIL_Timeout-- == 0)
      return ERROR;
  }
  if (NumByteToWrite == 0x01)
  {
    UTIL_Timeout = LONG_TIMEOUT;
    /* Prepare the register value to be sent */
    I2C_SendData(I2Cx, *pBuffer);

    /* Test on EV8 and clear it */
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
      if (UTIL_Timeout-- == 0)
        return ERROR;
    }

    /* End the configuration sequence */
    I2C_GenerateSTOP(I2Cx, ENABLE);
    return SUCCESS;
  }
  I2C_SendData(I2Cx, *pBuffer);
  pBuffer++;
  NumByteToWrite--;
  /* While there is data to be written */
  while (NumByteToWrite--)
  {
    while ((I2C_GetLastEvent(I2Cx) & 0x04) != 0x04);  /* Poll on BTF */
    /* Send the current byte */
    I2C_SendData(I2Cx, *pBuffer);
    /* Point to the next byte to be written */
    pBuffer++;

  }
  UTIL_Timeout = LONG_TIMEOUT;
  /* Test on EV8_2 and clear it, BTF = TxE = 1, DR and shift registers are
   empty */
  while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    if (UTIL_Timeout-- == 0) return ERROR;
  }
  /* Send STOP condition */
  I2C_GenerateSTOP(I2Cx, ENABLE);
  return SUCCESS;
}

uint8_t I2C_Check_SlaveAddr(I2C_TypeDef* I2Cx, uint8_t DeviceAddr)
{
	uint8_t   returnack = SUCCESS;

	UTIL_Timeout = FLAG_TIMEOUT;
	/* Send START condition */
	I2C_GenerateSTART(I2Cx, ENABLE);
	/* Test on EV5 and clear it */
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
	{
		if (UTIL_Timeout-- == 0) return ERROR;
	}
	/* Send slave address for write */
	I2C_Send7bitAddress(I2Cx, DeviceAddr, I2C_Direction_Transmitter);

	UTIL_Timeout = FLAG_TIMEOUT;
	/* Test on EV6 and clear it */
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
	{

		if (UTIL_Timeout-- == 0)
		{
		  I2C_ClearFlag(I2Cx,I2C_FLAG_BUSY|I2C_FLAG_AF);
		  returnack = ERROR;
		  break;
		}
	}
	I2C_GenerateSTOP(I2Cx, ENABLE);

	return returnack;
}

uint8_t HW_I2C_UTIL_Write(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr, uint8_t pBuffer)
{
	return HW_I2C_Write(I2Cx, DeviceAddr<<1,RegisterAddr,1,&pBuffer);
}

uint8_t HW_I2C_UTIL_Read(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr)
{
	uint8_t	pBuffer;

	HW_I2C_Read(I2Cx, DeviceAddr<<1,RegisterAddr,1,&pBuffer);

	return pBuffer;
}

void HW_I2C_UTIL_Write_Multi(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr, uint16_t NumByteToWrite, uint8_t* pBuffer)
{
	HW_I2C_Write(I2Cx, DeviceAddr<<1,RegisterAddr,NumByteToWrite,pBuffer);
}

uint8_t HW_I2C_UTIL_Read_Multi(I2C_TypeDef* I2Cx, uint8_t DeviceAddr, uint8_t RegisterAddr, uint8_t ReadCount, uint8_t (*pReadData))
{
	HW_I2C_Read(I2Cx, DeviceAddr<<1,RegisterAddr,ReadCount,pReadData);

	return 1;
}

uint8_t HW_I2C_UTIL_Check_SlaveAddr(I2C_TypeDef* I2Cx)
{
	uint8_t i = 0x00;

	for(i = 0; i < 255; i++)
	{
		if(I2C_Check_SlaveAddr(I2Cx, i) == SUCCESS)
		{
			return i;
		}
	}
	return i;
}
반응형

+ Recent posts