Skip to main content

Building a car battery data logger Part 3: How to apply timer interrupt and USART transmission

Set up of timer

This part shows how to use timer interrupt to record measured data (ADC results) with defined time period. In this project, TIM2 is used. According to the datasheet, STM32F407 VGT6 is using APB1 Bus giving 84Mhz TIM2 clock. To make 1ms timer interrupt with the configuration of TIM_TimeBaseStructure.TIM_Prescaler = 0,

Fp = (84MHz) / ((0 + 1) * (83999 + 1) = 1kHz

         Tp = 1 / Fp = 1ms

Where Tp and Fp are time period of timer interrupt in second and corresponding frequency respectively. Therefore, the value of TIM_TimeBaseStructure.TIM_Period should be 83999 in order to have a 1ms timer interrupt. The following code realizes a timer that introduces an interrupt every 1ms.

void TIM2_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 83999;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode + TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure;

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}

UART transmission

According to the STM32F4 reference manual, PA9 and PA10 pins can be used as TX and RX respectively under UART1.

To start UART transmission, the following needs to be done:

  • Enable GPIO, UART clocks
  • Configure the GPIO pins as AF (Alternate function Mode for connections to various peripherals)
  • Configure baud rate

This can be done using the following code

void USART1_Config()
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);


GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);

USART_InitStructure.USART_BaudRate = 9600;
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_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);

USART_Cmd(USART1, ENABLE);
}

Once the interrupt introduced by TIM2, ADC_Buffer[] with a size of 1000 will load the latest measured values into itself. Afterwards, 500 sets of data stored in ADC_Buffer[] will be sent out through UART. The details of code are shown as below.

void TIM2_IRQHandler()
{
while (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET & i < 500)
{
TIM_ClearITPendingBit(TIM2, /*TIM_IT_Update*/ TIM_FLAG_Update);
ADC_Buffer[i*2] = ADCConvertedValue[0]*3300/0xFFF; //Convert to mV
ADC_Buffer[i*2+1] = ADCConvertedValue[1]*3300/0xFFF; //Convert to mA
i++;
}
//USART transmissionn
for(int j = 0; j < 500; j++)
{
putnumber(ADC_Buffer[j*2]);
putnumber(j*2);
USART_SendData(USART1, '\n');
for(int k =0; k <50000; k++);

putnumber(ADC_Buffer[j*2+1]);
putnumber(j*2+1);
USART_SendData(USART1, '\n');
for(int k =0; k <50000; k++);
}

//Data to SD card
//sdData();
i = 0;
}

One point worth mentioning is that the values stored in ADC_Buffer[] are in the format of an integer. However, USART usually required a character format and send or receive data in a byte. The transformation from integer to character can be realized by putnumber() shown as below

void putnumber(uint32_t x)
{
char value[10]; //a temp array to hold results of conversion
int j = 0; //loop index

do
{
value[j++] = (char) (x % 10) + '0'; //convert interger to character
x /= 10;
} while (x);

while(j) //send data
{
USART_SendData(USART1,value[--j]);
for(int k =0; k < 50000; k++); // Delay
}
}

In the next part, I will take about how to create a php file to receive sockets (ADC results data) and load those data into a database for plotting line chart afterwards.

Cheukngai has not written a bio yet…
DesignSpark Electrical Logolinkedin