ADC independent DMA (2)

2016. 8. 14. 14:19 from MCU/ARM

ADC DMA


 STM32은 AVR과 다르게 conversion trigger 설정만 되어 있으면 ADC conversion이 자동으로 이루어진다. 자세한 것은 모르겠지만 ADC conversion trigger는 interrupt 우선순위가 매우 높은 것 같다. 실험 해본 것은 DMA 완료 인터럽트를 enable 시켜놓고 DMA interrupt routine 안에서 무한 반복문을 실행 시킨 후에 ADC conversion이 이루어지는지 해봤는데 결과는 interrupt routine 안에 서도 ADC conversion은 계속 이루어졌다.

 또한 AVR과 다른 점이 ADC converted value를 가져오는 방식이다. 라이브러리에 있는 ADC_GetConversionValue(), 

ADC_GetDualModeConversionValue() 그리고 ADC_GetInjectedConversionValue()함수를 이용하여 ADC converted value를 가져올 수도 있지만 DMA(Direct Memory Access)기능을 이용하여 ADC peripheral에 있는 값을 memory로 바로 가져올 수 있다. 아래서 보겠지만 DMA 설정을 enable 시켜 놓으면 conversion 후에 DMA request가 발생하여 자동으로 converted value가 destination location으로 저장된다. 즉, 다른 interrupt들에 방해 받지 않고 ADC_DR 값을 옮김으로써 data 손실이 없어진다.




ADC independent DMA 예제


-------------------------------------- my header --------------------------------------

#ifndef __MYDEFINE_H__

#define __MYDEFINE_H__


#include "stdio.h"

#include "stm32f10x_rcc.h"

#include "stm32f10x_usart.h"

#include "stm32f10x_gpio.h"

#include "stm32f10x_dma.h"

#include "stm32f10x_adc.h"


/* RCC header */

void RCC_Configuration();


/* GPIO header */

#define GPIO_LED1_PIN GPIO_Pin_9

#define GPIO_LED GPIOB

#define LED_GPIO_Clk RCC_APB2Periph_GPIOB

void test_led();

void test_led_togle();

void GPIO_Configuration();



/* USART header */

#define USART_GPIO_Clk RCC_APB2Periph_GPIOA

#define GPIO_USART GPIOA

#define GPIO_USART_Rx_Pin GPIO_Pin_10

#define GPIO_USART_Tx_Pin GPIO_Pin_9

void USART1_Init();

void SerialPutChar (uint8_t c);

void Serial_PutString(uint8_t *s);

uint8_t USART_GetCharacter(USART_TypeDef* usart_p);

int fputc(int ch, FILE *f);



/* ADC header */

#define ADC_GPIO_Clk RCC_APB2Periph_GPIOA

#define ADC_Clk RCC_APB2Periph_ADC1

#define GPIO_ADC GPIOA

#define GPIO_ADC_IN0 GPIO_Pin_0

void ADC_Configuration();



/* NVIC header */

//void NVIC_Configuration(void);



/* DMA header */

#define ADC1_DR_Address    ((uint32_t)0x4001244C)

#define DMA_Clk RCC_AHBPeriph_DMA1

void DMA_Configuration();



#endif



-------------------------------------- main.c --------------------------------------

#include "mydefine.h"


static void delay_int_count(volatile unsigned int nTime){     

  for(;nTime>0;nTime--);         

}

void delay_1_second(void){

  delay_int_count(806596);

}

void delay_100_milli_second(void){

  delay_int_count(80660);

}


extern __IO uint16_t ADCConvertedValue;


void main()

{

  /* RCC configuration */

  RCC_Configuration();

  /* GPIO configuration */

  GPIO_Configuration();

  /* NVIC configuration */

//  NVIC_Configuration();

  /* DMA configuration */

  DMA_Configuration();

  /* ADC configuration */

  ADC_Configuration();

  /* USART configuration */

  USART1_Init();

    

  

  while(1)

  {

    printf("ADCConvertedValue : %d \r\n", ADCConvertedValue);

  }

}


-------------------------------------- RCC.c --------------------------------------

#include "mydefine.h"

void RCC_Configuration()
{
  /* ADCCLK = PCLK2/6 */
  RCC_ADCCLKConfig(RCC_PCLK2_Div6); 
  
  /* Enable led GPIO clock enable */
  RCC_APB2PeriphClockCmd(LED_GPIO_Clk, ENABLE);
  
  /*  USART clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  RCC_APB2PeriphClockCmd(USART_GPIO_Clk, ENABLE);
  
  /* Enable ADC1 clock */
  RCC_APB2PeriphClockCmd(ADC_GPIO_Clk | ADC_Clk, ENABLE);
  
  /* Enable DMA1 clock */
  RCC_AHBPeriphClockCmd(DMA_Clk, ENABLE);
}


-------------------------------------- GPIO.c --------------------------------------

#include "mydefine.h"

#include "stm32f10x_gpio.h"


void GPIO_Configuration()

{

  GPIO_InitTypeDef GPIO_InitStructure;

   

  /* Configure gpio as output : LED1 */

  GPIO_InitStructure.GPIO_Pin = GPIO_LED1_PIN;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

  GPIO_Init(GPIO_LED, &GPIO_InitStructure);

  

  /* Configure USARTx_Tx as alteranate function push-pull */

  GPIO_InitStructure.GPIO_Pin = GPIO_USART_Tx_Pin;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  GPIO_Init(GPIO_USART, &GPIO_InitStructure);

  

  /* Configure USARTx_Rx as input floating */

  GPIO_InitStructure.GPIO_Pin = GPIO_USART_Rx_Pin;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

  GPIO_Init(GPIO_USART, &GPIO_InitStructure);

  

  /* Configure PA.00 (ADC Channel0) as analog input -------------------------*/

  GPIO_InitStructure.GPIO_Pin = GPIO_ADC_IN0;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;

  GPIO_Init(GPIO_ADC, &GPIO_InitStructure);

}


void test_led()

{

  GPIO_LED->BRR |= GPIO_LED1_PIN;

}

    


void test_led_togle()

{

  GPIO_LED->BRR |= GPIO_LED1_PIN;

  GPIO_LED->BSRR |= GPIO_LED1_PIN;  

}



-------------------------------------- DMA.c --------------------------------------

#include "mydefine.h"



__IO uint16_t ADCConvertedValue;


void DMA_Configuration()

{

  DMA_InitTypeDef DMA_InitStructure;

  

  /* DMA1 channel1 configuration ----------------------------------------------*/

  DMA_DeInit(DMA1_Channel1);

  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;

  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

  DMA_InitStructure.DMA_BufferSize = 1;

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

  DMA_InitStructure.DMA_Priority = DMA_Priority_High;

  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

  DMA_Init(DMA1_Channel1, &DMA_InitStructure);

  

  /* Enable DMA1 channel1 */

  DMA_Cmd(DMA1_Channel1, ENABLE);

}



-------------------------------------- ADC.c --------------------------------------

#include "mydefine.h"


void ADC_Configuration()

{

  ADC_InitTypeDef  ADC_InitStructure;

  

  /* ADC1 configuration ------------------------------------------------------*/

  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;

  ADC_InitStructure.ADC_ScanConvMode = ENABLE;

  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfChannel = 1;

  ADC_Init(ADC1, &ADC_InitStructure);


  /* ADC1 regular channel 0 configuration */ 

  ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);


  /* Enable ADC1 DMA */

  ADC_DMACmd(ADC1, ENABLE);

  

  /* Enable ADC1 */

  ADC_Cmd(ADC1, ENABLE);


  /* Enable ADC1 reset calibration register */   

  ADC_ResetCalibration(ADC1);

  /* Check the end of ADC1 reset calibration register */

  while(ADC_GetResetCalibrationStatus(ADC1));


  /* Start ADC1 calibration */

  ADC_StartCalibration(ADC1);

  /* Check the end of ADC1 calibration */

  while(ADC_GetCalibrationStatus(ADC1));

     

  /* Start ADC1 Software Conversion */ 

  ADC_SoftwareStartConvCmd(ADC1, ENABLE);

}


ADC1 configuration



 위 비트는 ADC Dual mode 사용을 결정한다. Dual mode에는 두 가지 ADC를 통해서 다양한 모드를 만들 수 있는데 이것은 다음에 보도록 한다. 지금 우리는 single mode를 사용할 것이기 때문에 'DUALMOD[3:0]' 에 0000 - independent mode를 선택한다. 




 위 비트는 Scan mode를 결정한다. 사실 single mode에 channel 한 개를 쓰기 때문에 scan mode은 아무 의미 없다. scan mode는 ADC_SQRx register를 통해서 group 지어진 것을 scan 하여 자동적으로 conversion하도록 하는 것이다.




 위 비트는 Continuous conversion을 결정한다. Single conversion과 반대 개념으로 써 CONT bit가 1이고 , SCAN mode가 Disable 이면 channel conversion 이후에 다음 channel 이 자동으로 conversion 된다. SCAN mode가 Enable 이면 group 전체를 conversion한 이후에 다시 group conversion이 이루어진다.




 위 비트는 external trigger 종류를 무엇으로 할지 결정해준다. ADC main feature(1)에서 설명했지만 STM32 ADC는 무조건 external trigger가 있고 external trigger를 결정하므로써 trigger가 발동된다. 우리는 software적으로 trigger를 발생시킬 것이기 때문에 'EXTSEL[2:0]' 에 (111) - SWSTART 를 선택한다.




 위 비트는 Data alignment를 하위 비트에서 부터 할지 상위 비트에서부터 할지 결정한다. 별 내용 없다. 대부분 오른쪽에서 부터 할당함.




 위 비트는 Regular channel 총 몇개 conversion하는지 결정한다. 우리는 channel 한개만 사용하므로 'L[3:0]' 에 (0000) - 1 conversion을 선택한다.




ADC channel configuration


 이 register는 ADC channel 우선순위를 결정한다. 라이브러리에 있는 ADC_RegularChannelConfig() 함수를 통해서 아주 쉽게 ADC channel 우선순위를 결정할 수 있다.




ADC DMA


ADC_CR2 register에 8번 비트인데 이 비트는 ADC DMA 사용을 결정한다. DMA 요청은 end of conversion of regular channel 에서 발생한다. 




ADC ENABLE


 위 ADC_CR2 register에 0번 비트인데 ADC on/off 를 결정한다. 




ADC calibration


 

 위 ADC_CR2 register에 2,3번 비트인데 calibration on/off를 결정한다. calibration은 ADC 정확성을 증가시켜주는데 ADC power on(ADON set) 시키고 나서 calibration을 한번은 꼭 해주는 것이 좋다.




ADC external trigger


 위 ADC_CR2 register에 20번, external trigger on/off 결정한다.


---------------------------------------------------------------------------------------------


'MCU > ARM' 카테고리의 다른 글

TIM frequency, cycle 결정 방법  (0) 2017.04.07
ADC main feature(1)  (0) 2016.08.13
SPI 통신 (3)  (0) 2016.08.11
SPI 통신 (2)  (0) 2016.08.11
SPI 통신 (1)  (0) 2016.08.11
Posted by 나무길 :

ADC main feature(1)

2016. 8. 13. 20:54 from MCU/ARM

STM32F10X ADC main features


Regular and Injected conversions


 STM32 microcontrollers support two ADC conversion modes: regular and injected. Regular mode is what we used to see in many types of microcontroller (all channels share same data register). 사실 regular and injected conversions는 어떤 모드라고 하기 보다는 conversion 방법 중 하나라고 할 수 있다.


 conversion을 interrupt 없이 하는 것이 regular conversion이라 생각하면 되고 conversion 도중에 다른 conversion이 생겨서 interrupt와 비슷하게 중간에 끼어들어서 conversion이 가능한 것이 injected conversion이라고 생각하면 되겠다. 예를 들어서 아래에 설명하겠지만 ADC mode 중에 continuous mode가 있는데 continuous mode를 사용하면서 regular 하게 conversion 할것인가 아니면 Injected conversion을 할 것인가 선택할 수 있다.

 If normal group conversion is going on and injected conversion is triggered, then normal conversion is halted, then injected conversion is performed. When done normal conversion is resumed. Injected conversion channels have their own result registers and offset registers. Offset can store value that will be automatically deducted from ADC result.




Channel selection


 There are 16 multiplexed channels. It is possible to organize the conversions in two groups : regular and injected. A group consists of a sequence of conversions which can be done on any channel and in any order. For instance, it is possible to do the conversion in the following order: Ch3, Ch8, Ch2, Ch2, ch0, ch2, Ch2, Ch15.


- The regular group is composed of up to 16 conversions. The regular channels and their order in the conversion sequence must be selected in the ADC_SQRx registers. The total number of conversions in the regular group must be written in the L[3:0] bits in the ADC_SQR1 register.

- The injected group is composed of up to 4 conversions. The injected channels and their order in the conversion sequence must be selected in the ADC_JSQR register. The total number of conversions in the injected group must be written in the L[1:0]bits in the ADC_JSQR register.


* If the ADC_SQRx or ADC_JSQR registers are modifed during a conversion, the current conversion is reset and a new start pulse is sent to the ADC to convert the new chosen group.


ADC 설정을 끝내고 ADC ENABLE 시킨 후 ADC_RegularChannelConfig() 함수를 이용하여 ADC_SQRx register를 변경하면 원하는 결과가 안된다. ADC conversion 도중에 ADC_SQRx register 건들지 말것.




Timing diagram


 As shown below figure, the ADC needs a stabiliztion time of Tstab before it starts converting accurately. After the start of ADC conversion and after 14 clock cycles, the EOC flag is set and the 16bit ADC Data register contains the result of the conversion. EOC flag를 이용하여 ADC 완료 interrupt를 쓸 수 있다.





Analog watchdog


 The AWD analog watchdog status bit is set if the analog voltage converted by the ADC is below a low threshold or a high threshold. These thresholds are programmed in the 12 least significant bits of the ADC_HTR and ADC_LTR 16-bit registers. An interrupt can be enabled by using the AWDIE bit in the ADC_CR1 register.


 The threshold value is independent of the alignment selected by the ALIGN bit in the ADC_CR2 register. The comparison is done before the alignment. 이 문장이 아주 중요한 문장인데 ADC conversion이 끝난 후 ADC_DR 에 converted value가 저장되기 전에 threshold값과 비교한다는 말이다. 이것은 Independent mode(Dual mode를 안 쓰는 것)에서도 유용하게 쓸 수 있지만 더욱 유용하게 쓰이는 곳은 Dual mode이다. Dual mode에서 ADC1과 ADC2의 결과물인 converted value는 같은 레지스터에 저장된다. 32 bit register를 반으로 나누어서 상위 16비트는 ADC2 converted value, 하위 16비트는 ADC1 converted value로 저장된다. 만약 이때 threshold가 ADC_DR과 값을 비교한다면 Dual mode에서는 whatchdog를 쓸 수 없게 될 것이다. 32bit 값(ADC_DR)과 16bit 값(threshold)을 비교하면 당연히 항상 32bit 값이 크기 때문에(물론 복잡한 과정을 거치면 가능하겠지만..).


 쉽게 말해서 converted value가 ADC_DR에 저장되기 전에 threshold와 비교를 하므로써 Dual mode에서도 watchdog를 사용할 수 있게 되는 것이다.


 The analog watchdog can be enabled on one or more channels by configuring the ADC_CR1 register.





Conversion on external trigger


 Conversion can be triggered by an external event (e.g. timer capture, EXTI line). if the EXT-TRIG control bit is set then external events are able to trigger a conversion. The EXT-SEL[2:0] and JEXTSEL[2:0] control bits allow the application to select decide which out of 8 possible events can trigger conversion for the regular and injected groups.


* When an external trigger is selected for ADC regular or injected conversion, only the rising edge of the signal can start the conversion.


 'external trigger'라는 말을 조심해야 하는데 잘못 생각하면 외부에서 주는 트리거. 즉, 내부에서 트리거(SWSTART라는 것이 있는데 딱히 internal trigger라는 말은 없지만 software start라고 하면 내부 트리거 같기 때문에 적어봄)를 주면 따로 트리거를 안줘도 될 것이라 생각하게 된다. 하지만 STM32 ADC는 conversion을 하기 위해서는 무조건 external trigger를 주고 시작해야 한다. external trigger를 주고 external trigger의 종류를 선택하게 되는데 그 종류 중 하나가 

SWSTART인 것이다. 그래서 나중에 코드를 보겠지만 trigger를 주는 라이브러리 함수 중에 ADC_ExternalTrigConvCmd() 그리고 ADC_SoftwareStartConvCmd() 두 개가 있는데 ADC_SoftwareStartConvCmd() 이 함수 내부를 보게 되면 아래 ADC_CR2 register에 SWSTART bit, EXTTRIG bit  두 개를 전부 set 시켜준다.





ADC mode


 ADC는 다양한 mode가 존재하는데 각 mode에 대한 설명을 하고 mode별 차이점을 적는다. 또한 잘못 생각하기 쉬운점도 같이 알아보기.


Single conversion mode


 In Single conversion mode the ADC does one conversion. This mode is started either by setting the ADON bit in the ADC_CR2 register (for a regular channel only) or by external trigger (for a regular or injected channel), while the CONT bit is 0. 이 모드는 Continuous mode와 반대되는 개념으로써 한번 conversion이 이뤄진 후에 다음 conversion을 하기 위해서 다시 stabilization(ADC 초기 setting이라고 생각) 작업이 또 이루어진다. 계속해서 conversion 하기 전에 stabilization이 이루어짐. 


 Single mode와 Continuous mode 차이가 ADC channel을 group하느냐 안하느냐로 생각하면 안된다(group은 ADC_SQR register에서 정하는 것).  ADC channel을 group하여도 Single mode를 사용할 수 있고 Continuous mode를 사용할 수 있다. 자세한 설명은 아래서.


Once the conversion of the selected channel is complete:

@ If a regular channel was converted:

 - The converted data is stored in the 16-bit ADC_DR register

 - The EOC (End Of Conversion) flag is set

 - and an interrupt is generated if the EOCIE is set.

@ If a injected channel was converted:

 - The converted data is stored in the 16-bit ADC_DRJ1 register

 - The JEOC (End Of Conversion Injected) flag is set

 - and an interrupt is generated if the JEOCIE bit is set.

The ADC is then stopped.



Continuous conversion mode


 In continuous conversion mode ADC starts another conversion as soon as it finishes one. This mode is started either by external trigger of by setting the ADON bit in the ADC_CR2 register, while the CONT bit is 1. 처음에 한번만 stabilization이 이루어지고 그 다음 conversion부터는 stabilization이 없다.


After each conversion:

@ If a regular channel was converted:

 - The converted data is stored in the 16-bit ADC_DR register

 - The EOC (End Of Conversion) flag is set

 - An interrupt is generated if the EOCIE is set

@ If an injected channel was converted:

 - The converted data is stored in the 16- bit ADC_DRJ1 register

 - The JEOC (End Of Conversion Injected) flag is set

 - An interrupt is generated if the JEOCIE bit is set.



Scan mode


 This mode is used to scan a group of analog channels.

Scan mode can be selected by setting the SCAN bit in the ADC_CR1 register. Once this bit is set, ADC scans all the cannels selected in the ADC_SQRx registers (for regular channels) or in the ADC_JSQR (for injected channels). A single conversion is performed for each channel of the group. After each end of conversion the next channel of the group is converted automatically. If the CONT bit is set, conversion does not stop at the last selected group channel but continues again from the first selected group channel.


 When using scan mode, DMA bit must be set and the direct memory access controller is used to transfer the converted data of regular group channels to SRAM after each update of the ADC_DR register.

The injected channel converted data is always stored in the ADC_JDRx registers.



Discontinuous mode


 Regular group과 Injected group에 대한 discontinuous mode가 존재하는데 거의 똑같기 때문에 regular group discontinuous mode만 설명.


 This mode is enabled by setting the DISCEN bit in the ADC_CR1 register. It can be used to convert a short sequence of n conversions (n <= 8) which is a part of the sequence of conversions selected in the ADC_SQRx registers. The value of n is specified by writing to the DISCNUM[2:0] bits in the ADC_CR1 register.


 When an external trigger occurs, it starts the next n conversions selected in the ADC_SQRx registers until all the conversions in the sequence are done. The total sequence length is defined by the L[3:0] bits in the ADC_SQR1 register. 이 모드는 Continuous mode의 반대도 아니고 Single mode의 반대도 아니다. 조금 특별한 모드인데 기본은 Continuous mode다. 지정된 group을 Continuous mode 처럼 conversion을 하되 group 전체를 sequence conversion 하는 것이 아닌 지정 갯수 만큼 sequence conversion 하는 것. 


 쉽게 말해서 원래 정해 놓은 group을 다시 쪼개서 conversion 한다고 생각하면 된다.


Example : n = 3, channels to be converted = 0, 1, 2, 3, 6, 7, 9, 10

1st trgger: sequence converted 0, 1, 2. An EOC event is generated at each conversion

2nd trigger: sequence converted 3, 6, 7. An EOC event is generated at each conversion

3nd trigger: sequence converted 9, 10. An EOC event is generated at each conversion

4nd trigger: sequence converted 0, 1, 2. An EOC event is generated at each conversion


 When a regular group is converted in discontinuous mode, no rollover will occure. When all sub groups are converted, the next trigger starts conversion of the first sub-group.



Dual ADC mode


 dual ADC mode 안에는 ADC 두 개를 어떻게 조합하는가에 따라서 10가지에 mode가 만들어진다. 각각 모드에 대한 설명은 따로 설명.


 In dual ADC mode the start of conversion is triggered alternately or simultaneously by the ADC1 master to the ADC2 slave, depending on the mode selected by the DUALMOD[2:0] bits in the ADC1_CR1 register.


* In dual mode, when configuring conversion to be triggered by an external event, the user must set the trigger for the master only and set a software trigger for the slave to prevent spurious triggers to start unwanted slave conversion. Hovever, external triggers must be enabled on both master and slave ADCs.


 아래 그림과 같이  dual ADC mode에서 ADC2(slave) conversion trigger는 ADC1(master) internal trigger에서 주고 있다. 따라서 ADC2는 external trigger를 enable 시키고 software start를 설정해 주면 된다. 그리고 ADC1에만 내가 원하는 trigger를 선택해서 (e.g. TIM trigger, SW trigger..) enable 시켜주면 됨.




참고 : RM0008 Reference manual

'MCU > ARM' 카테고리의 다른 글

TIM frequency, cycle 결정 방법  (0) 2017.04.07
ADC independent DMA (2)  (0) 2016.08.14
SPI 통신 (3)  (0) 2016.08.11
SPI 통신 (2)  (0) 2016.08.11
SPI 통신 (1)  (0) 2016.08.11
Posted by 나무길 :

SPI 통신 (3)

2016. 8. 11. 16:52 from MCU/ARM

SPI code



--------------------------------------------- header ---------------------------------------------



/* SPI header */

#define SPIy            SPI1

#define SPIy_GPIO       GPIOA

#define SPIy_SCKPin     GPIO_Pin_5

#define SPIy_MISOPin    GPIO_Pin_6

#define SPIy_MOSIPin    GPIO_Pin_7


#define DYMMY_BYTE      0x00



void GPIO_Configuration();


/* USART header */

#define GPIO_USART GPIOA

#define GPIO_USART_Rx_Pin GPIO_Pin_10

#define GPIO_USART_Tx_Pin GPIO_Pin_9


#define USARTy          USART1

#define USARTy_GPIO     GPIOA

#define USARTy_TxPin    GPIO_Pin_9

#define USARTy_RxPin    GPIO_Pin_10

#define USARTy_ClkPin   GPIO_Pin_8


void SerialPutChar (uint8_t c);

void Serial_PutString(uint8_t *s);

uint8_t USART_GetCharacter(USART_TypeDef* usart_p);

int fputc(int ch, FILE *f);

void USARTy_Init();



/* RCC header */

void RCC_Configuration();


/* SPI header */

void SPI_Configuration();



--------------------------------------------- RCC.c ---------------------------------------------


void RCC_Configuration()

{

  /* USART1, SPI1, USART1 SPI1 PORT clock enable */

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

}


--------------------------------------------- GPIO.c ---------------------------------------------


void GPIO_Configuration()

{

  GPIO_InitTypeDef GPIO_InitStructure;

   

  /* Configure USARTy TX and USARTy CK pins as alterante function push-pull */

  GPIO_InitStructure.GPIO_Pin = GPIO_USART_Tx_Pin | USARTy_ClkPin;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);

  

  /* Configure SPI1 pins : SCK, MISO and MOSI */

  GPIO_InitStructure.GPIO_Pin = SPIy_SCKPin | SPIy_MISOPin | SPIy_MOSIPin;

  GPIO_Init(SPIy_GPIO, &GPIO_InitStructure);

  

  /* Configure USARTy RX as input floating */

  GPIO_InitStructure.GPIO_Pin = USARTy_RxPin;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

  GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);

}


위 코드는 stm사에서 제공하는 예제 코드를 사용하여 SPI USART 통신을 성공한 code이다. 그런데 이상한 것은 SPI slave 입장에서는 SPIy_SCKPin과 SPIy_MOSIPin이 input mode를 사용해야 하지 않을까?? 나중에 다시 코드를 바꾼 후 실험해봐야겠다.


--------------------------------------------- USART.c ---------------------------------------------


/* Private define ------------------------------------------------------------*/

#define TxBufferSize1   (countof(TxBuffer1) - 1)

#define TxBufferSize2   (countof(TxBuffer2) - 1)


/* Private variables ---------------------------------------------------------*/

uint8_t TxBuffer1[] = "USART Synchronous Example: USARTy -> SPIy using TXE and RXNE Flags";

uint8_t TxBuffer2[] = "USART Synchronous Example: SPIy -> USARTy using TXE and RXNE Flags";

uint8_t RxBuffer1[TxBufferSize2];

uint8_t RxBuffer2[TxBufferSize1];

__IO uint8_t NbrOfDataToRead1 = TxBufferSize2;

__IO uint8_t NbrOfDataToRead2 = TxBufferSize1;

__IO uint8_t TxCounter1 = 0, RxCounter1 = 0;

__IO uint8_t TxCounter2 = 0, RxCounter2 = 0;


USART_InitTypeDef USART_InitStructure;

USART_ClockInitTypeDef USART_ClockInitStructure; 



void USARTy_Init()

{

  /* USARTy configuration ------------------------------------------------------*/

  /* USARTy configured as follow:

        - BaudRate = 115200 baud  

        - Word Length = 8 Bits

        - One Stop Bit

        - No parity

        - Hardware flow control disabled (RTS and CTS signals)

        - Receive and transmit enabled

        - USART Clock Enabled

        - USART CPOL: Clock is active High

        - USART CPHA: Data is captured on the second edge 

        - USART LastBit: The clock pulse of the last data bit is output to 

                         the SCLK pin

  */

  USART_ClockInitStructure.USART_Clock = USART_Clock_Enable;

  USART_ClockInitStructure.USART_CPOL = USART_CPOL_High;

  USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;

  USART_ClockInitStructure.USART_LastBit = USART_LastBit_Enable;

  USART_ClockInit(USARTy, &USART_ClockInitStructure);


  USART_InitStructure.USART_BaudRate = 115200;

  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(USARTy, &USART_InitStructure);

  

  /* Configure the USARTy */

  USART_Init(USARTy, &USART_InitStructure);


  /* Enable the USARTy */

  USART_Cmd(USARTy, ENABLE);

}


--------------------------------------------- SPI.c ---------------------------------------------


void SPI_Configuration()

{

  SPI_InitTypeDef SPI_InitStructure;


  SPI_StructInit(&SPI_InitStructure);


  SPI_I2S_DeInit(SPIy);


  /* SPIy Config */

  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

  SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;

  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_FirstBit = SPI_FirstBit_LSB;

  

  /* Configure SPIy */

  SPI_Init(SPIy, &SPI_InitStructure);


  /* SPIy enable */

  SPI_Cmd(SPIy, ENABLE);

}


--------------------------------------------- main.c ---------------------------------------------


void main()

{

  RCC_Configuration();  

  GPIO_Configuration();

  SPI_Configuration();

  USARTy_Init();

    

  while(NbrOfDataToRead2--)

  {

    /* Write one byte in the USARTy Transmit Data Register */

    USART_SendData(USARTy, TxBuffer1[TxCounter1++]);

    /* Wait until end of transmit */

    

    //while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

    

    while(USART_GetFlagStatus(USARTy, USART_FLAG_TC) == RESET)

    {

    }

    /* Wait the byte is entirely received by SPIy */  

    while(SPI_I2S_GetFlagStatus(SPIy, SPI_I2S_FLAG_RXNE) == RESET)

    {

    }

    /* Store the received byte in the RxBuffer2 */

    RxBuffer2[RxCounter2++] = SPI_I2S_ReceiveData(SPIy);

  }


  /* Clear the USARTy Data Register */

  USART_ReceiveData(USARTy);


  while(NbrOfDataToRead1--)

  {

    /* Wait until end of transmit */

    while(SPI_I2S_GetFlagStatus(SPIy, SPI_I2S_FLAG_TXE)== RESET)

    {

    }

    /* Write one byte in the SPIy Transmit Data Register */

    SPI_I2S_SendData(SPIy, TxBuffer2[TxCounter2++]);


    /* Send a Dummy byte to generate clock to slave */ 

    USART_SendData(USARTy, DYMMY_BYTE);

    /* Wait until end of transmit */

    while(USART_GetFlagStatus(USARTy, USART_FLAG_TC) == RESET)

    {

    }

    /* Wait the byte is entirely received by USARTy */

    while(USART_GetFlagStatus(USARTy, USART_FLAG_RXNE) == RESET)

    {

    }

    /* Store the received byte in the RxBuffer1 */

    RxBuffer1[RxCounter1++] = USART_ReceiveData(USARTy);

  }


  printf ("\r\nRxBuffer1 : %s \r\n", RxBuffer1);

  printf ("RxBuffer2 : %s \r\n", RxBuffer2);

  

  while(1)

  {

    

  }

}

'MCU > ARM' 카테고리의 다른 글

ADC independent DMA (2)  (0) 2016.08.14
ADC main feature(1)  (0) 2016.08.13
SPI 통신 (2)  (0) 2016.08.11
SPI 통신 (1)  (0) 2016.08.11
(*(volatile unsigned *))0x40021018 의미  (1) 2013.12.15
Posted by 나무길 :