cancel
Showing results for 
Search instead for 
Did you mean: 

stm32f103 bare metal programming issue - code only works if I set ADC_CAL twice in a row!

nje17
Associate

Hello all,

I have done my due diligence looking everywhere for an answer to this as well as trying many different variations of the code I will post below. My issue is my LCD will not display the ADC value from a trimpot any other way. I thought at first maybe I was not giving the ADC_ON enough clock cycles as per the rm. So I added 1, 2, 3, all the way to like 16 asm("NOP")  commands and I tried rearranging all manner of things in the offending portion. The ONLY solution I could figure out was using the following twice:

ADC1-> CR2 |= ADC_CR2_CAL;

ADC1-> CR2 |= ADC_CR2_CAL;

 

-or, similarly-

 

ONCE in the multiple bitwise operation and ONCE a few lines below:

 

// ENSURE ADC IS ON for CAL for 2cycles!

ADC1 -> CR2 |= (ADC_CR2_ADON | ADC_CR2_CONT | ADC_CR2_CAL); //this is = 1 cycle

//wait for ADC to turn on!

while((ADC1-> CR2 & ADC_CR2_ADON) == 0){} //this is = 2 cycles!

//It should be on by now

ADC1-> CR2 |= ADC_CR2_CAL;

 

 

FULL CODE:

 

#include "main.h"

#include "LCDFunctions.h"

 

 

void SystemClock_Config(void);

 

int main(void)

{

 

HAL_Init();

SystemClock_Config();

InitializePortsForLCD();

InitializeTheLCDDisplayAndModes();

 

 

// Enable the ADC Clock Interface

RCC-> APB2ENR |= RCC_APB2ENR_ADC1EN;

//Turn on the HighSpeedInternal 8Mhz clock

RCC-> CR |= RCC_CR_HSION;

//wait for the clock to turn on

while((RCC->CR & RCC_CR_HSION) == 0){}

 

// Set ADC clock to HSI divided by 2

RCC->CFGR &= ~RCC_CFGR_ADCPRE; // Clear ADC prescaler bits

RCC->CFGR |= RCC_CFGR_ADCPRE_DIV2; // HSI divided by 2, now clock is 4Mhz

// Set ADC sampling time for channel 16 (Temperature sensor) in SMPR1 register

// While in SMPR register SMPx must be chosen for appropriate channel

// e.g. SMPR1_SMP10 for Channel 10 SMPR2_SMP0 for Channel 0

 

/*// Clear previous bits for channel sampling rate

ADC1->SMPR1 &= ~ADC_SMPR1_SMP16;

// Longest sampling time for channel 16 (Temperature sensor)

ADC1->SMPR1 |= (ADC_SMPR1_SMP16_2 | ADC_SMPR1_SMP16_1 | ADC_SMPR1_SMP16_0);*/

 

ADC1->SMPR2 &= ~ADC_SMPR2_SMP7;

// Longest sampling time for channel 7 (Voltage Divider)

ADC1->SMPR2 |= (ADC_SMPR2_SMP7_2 | ADC_SMPR2_SMP7_1 | ADC_SMPR2_SMP7_0);

 

/*//Turn on channel 16

//10000 is binary for decimal 16 so for channel 16 set the 5th bit of 4:0

ADC1->SQR3 |= ADC_SQR3_SQ1_4;

//Turn on Temperature Sensor and Voltage Reference this could be set at the same

//time as ADON to minimize delay

ADC1->CR2 |= ADC_CR2_TSVREFE;*/

 

//Align the data before beginning the conversion (reSETTING THE BIT IS right ALIGN)

ADC1->CR2 &= ~(ADC_CR2_ALIGN);

 

//Clear previous bits

ADC1->SQR3 &= ~(ADC_SQR3_SQ1);

//Turn on Channel 7 in binary 00111

ADC1->SQR3 |= (ADC_SQR3_SQ1_2 | ADC_SQR3_SQ1_1 | ADC_SQR3_SQ1_0);

 

 

 

// ENSURE ADC IS ON for CAL for 2cycles!

ADC1 -> CR2 |= (ADC_CR2_ADON | ADC_CR2_CONT); //this is = 1 cycle

//wait for ADC to turn on!

while((ADC1-> CR2 & ADC_CR2_ADON) == 0){} //this is = 2 cycles!

//It should be on by now

 

//TRYING CAL AGAIN...HMMM THIS SEEMS TO WORK

ADC1-> CR2 |= ADC_CR2_CAL;

ADC1-> CR2 |= ADC_CR2_CAL;

 

LCDSendAString("ADC CALing", 1, 1);

 

while((ADC1 -> CR2 & ADC_CR2_CAL) != 0){}

//Wait to finish CAL

 

LCDSendAString("ADC CALd", 1, 1);

 

 

/*//Write a message saying everything is ready

LCDSendAString(" Cel", 1, 1);

LCDSendAString("ADC", 6, 2);

// Cal'd 25C V=1.42 so 1.42/3.3V = 1762/4095 ADC values

float V25 = 1762.0000;

// in mV/degreeC per reference manual

float AVG_SLOPE = 0.0043; // 43mV/dC

// .0043/3.3 = 5.3359/4095

float AVGSlopeConvertedToADC = (AVG_SLOPE/3.3)*4095;*/

 

LCDSendAString("Trim Val", 1, 1);

 

 

while (1)

{

//Start the ADC conversion continiuously

ADC1->CR2 |= ADC_CR2_SWSTART;

//Wait for end of conversion flag

while((ADC1->SR & ADC_SR_EOC) == 0){}

//Display the ADC Data Register on the LCD

float ADCData = ADC1->DR;

/*

//Renamed to inputVoltage to match stm32 reference manual

float inputVoltage = ADCData;

float temperature = (((V25 - inputVoltage)/AVGSlopeConvertedToADC)+25);

LCDSendAFloat(temperature, 4, 1, 1);*/

 

//Remove artifacts

LCDSendAString(" ", 1, 2);

LCDSendAnInteger(ADCData, 4, 1, 2);

//set a delay so there the LCD isn't a blur

nonExactTimeDelay(1000000);

}

 

}

 

 

void SystemClock_Config(void)

{

RCC_OscInitTypeDef RCC_OscInitStruct = {0};

RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 

/** Initializes the RCC Oscillators according to the specified parameters

* in the RCC_OscInitTypeDef structure.

*/

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;

RCC_OscInitStruct.HSIState = RCC_HSI_ON;

RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;

RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

{

Error_Handler();

}

 

/** Initializes the CPU, AHB and APB buses clocks

*/

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;

RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

 

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)

{

Error_Handler();

}

}

 

/* USER CODE BEGIN 4 */

 

/* USER CODE END 4 */

 

/**

* @brief This function is executed in case of error occurrence.

* @retval None

*/

void Error_Handler(void)

{

/* USER CODE BEGIN Error_Handler_Debug */

/* User can add his own implementation to report the HAL error return state */

__disable_irq();

while (1)

{

}

/* USER CODE END Error_Handler_Debug */

}

 

#ifdef USE_FULL_ASSERT

/**

* @brief Reports the name of the source file and the source line number

* where the assert_param error has occurred.

* @param file: pointer to the source file name

* @param line: assert_param error line source number

* @retval None

*/

void assert_failed(uint8_t *file, uint32_t line)

{

/* USER CODE BEGIN 6 */

/* User can add his own implementation to report the file name and line number,

ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

/* USER CODE END 6 */

}

#endif /* USE_FULL_ASSERT */

 

1 ACCEPTED SOLUTION

Accepted Solutions
nje17
Associate

Well, I'll be damned - right after I post the question, I figure it out... it works fine if you set up the sampling rate and all that jazz after the CAL portion... didn't think that would matter much. Here is the full code for anyone else experiencing similar issues:

#include "main.h"

#include "LCDFunctions.h"

 

 

void SystemClock_Config(void);

 

int main(void)

{

 

HAL_Init();

SystemClock_Config();

InitializePortsForLCD();

InitializeTheLCDDisplayAndModes();

 

 

// Enable the ADC Clock Interface

RCC-> APB2ENR |= RCC_APB2ENR_ADC1EN;

//Turn on the HighSpeedInternal 8Mhz clock

RCC-> CR |= RCC_CR_HSION;

//wait for the clock to turn on

while((RCC->CR & RCC_CR_HSION) == 0){}

 

// Set ADC clock to HSI divided by 2

RCC->CFGR &= ~RCC_CFGR_ADCPRE; // Clear ADC prescaler bits

RCC->CFGR |= RCC_CFGR_ADCPRE_DIV2; // HSI divided by 2, now clock is 4Mhz

// Set ADC sampling time for channel 16 (Temperature sensor) in SMPR1 register

// While in SMPR register SMPx must be chosen for appropriate channel

// e.g. SMPR1_SMP10 for Channel 10 SMPR2_SMP0 for Channel 0

 

/*// Clear previous bits for channel sampling rate

ADC1->SMPR1 &= ~ADC_SMPR1_SMP16;

// Longest sampling time for channel 16 (Temperature sensor)

ADC1->SMPR1 |= (ADC_SMPR1_SMP16_2 | ADC_SMPR1_SMP16_1 | ADC_SMPR1_SMP16_0);*/

 

// ENSURE ADC IS ON for CAL for 2cycles!

ADC1 -> CR2 |= (ADC_CR2_ADON | ADC_CR2_CONT); //this is = 1 cycle

//wait for ADC to turn on!

while((ADC1-> CR2 & ADC_CR2_ADON) == 0){} //this is = 2 cycles!

//It should be on by now

 

//TRYING CAL AGAIN...HMMM THIS SEEMS TO WORK

ADC1-> CR2 |= ADC_CR2_CAL;

//ADC1-> CR2 |= ADC_CR2_CAL;

 

LCDSendAString("ADC CALing", 1, 1);

 

while((ADC1 -> CR2 & ADC_CR2_CAL) != 0){}

//Wait to finish CAL

 

LCDSendAString("ADC CALd", 1, 1);

 

ADC1->SMPR2 &= ~ADC_SMPR2_SMP7;

// Longest sampling time for channel 7 (Voltage Divider)

ADC1->SMPR2 |= (ADC_SMPR2_SMP7_2 | ADC_SMPR2_SMP7_1 | ADC_SMPR2_SMP7_0);

 

/*//Turn on channel 16

//10000 is binary for decimal 16 so for channel 16 set the 5th bit of 4:0

ADC1->SQR3 |= ADC_SQR3_SQ1_4;

//Turn on Temperature Sensor and Voltage Reference this could be set at the same

//time as ADON to minimize delay

ADC1->CR2 |= ADC_CR2_TSVREFE;*/

 

//Align the data before beginning the conversion (reSETTING THE BIT IS right ALIGN)

ADC1->CR2 &= ~(ADC_CR2_ALIGN);

 

//Clear previous bits

ADC1->SQR3 &= ~(ADC_SQR3_SQ1);

//Turn on Channel 7 in binary 00111

ADC1->SQR3 |= (ADC_SQR3_SQ1_2 | ADC_SQR3_SQ1_1 | ADC_SQR3_SQ1_0);

/*//Write a message saying everything is ready

LCDSendAString(" Cel", 1, 1);

LCDSendAString("ADC", 6, 2);

// Cal'd 25C V=1.42 so 1.42/3.3V = 1762/4095 ADC values

float V25 = 1762.0000;

// in mV/degreeC per reference manual

float AVG_SLOPE = 0.0043; // 43mV/dC

// .0043/3.3 = 5.3359/4095

float AVGSlopeConvertedToADC = (AVG_SLOPE/3.3)*4095;*/

 

LCDSendAString("Trim Val", 1, 1);

 

 

while (1)

{

//Start the ADC conversion continiuously

ADC1->CR2 |= ADC_CR2_SWSTART;

//Wait for end of conversion flag

while((ADC1->SR & ADC_SR_EOC) == 0){}

//Display the ADC Data Register on the LCD

float ADCData = ADC1->DR;

/*

//Renamed to inputVoltage to match stm32 reference manual

float inputVoltage = ADCData;

float temperature = (((V25 - inputVoltage)/AVGSlopeConvertedToADC)+25);

LCDSendAFloat(temperature, 4, 1, 1);*/

 

//Remove artifacts

LCDSendAString(" ", 1, 2);

LCDSendAnInteger(ADCData, 4, 1, 2);

//set a delay so there the LCD isn't a blur

nonExactTimeDelay(1000000);

}

 

}

 

View solution in original post

1 REPLY 1
nje17
Associate

Well, I'll be damned - right after I post the question, I figure it out... it works fine if you set up the sampling rate and all that jazz after the CAL portion... didn't think that would matter much. Here is the full code for anyone else experiencing similar issues:

#include "main.h"

#include "LCDFunctions.h"

 

 

void SystemClock_Config(void);

 

int main(void)

{

 

HAL_Init();

SystemClock_Config();

InitializePortsForLCD();

InitializeTheLCDDisplayAndModes();

 

 

// Enable the ADC Clock Interface

RCC-> APB2ENR |= RCC_APB2ENR_ADC1EN;

//Turn on the HighSpeedInternal 8Mhz clock

RCC-> CR |= RCC_CR_HSION;

//wait for the clock to turn on

while((RCC->CR & RCC_CR_HSION) == 0){}

 

// Set ADC clock to HSI divided by 2

RCC->CFGR &= ~RCC_CFGR_ADCPRE; // Clear ADC prescaler bits

RCC->CFGR |= RCC_CFGR_ADCPRE_DIV2; // HSI divided by 2, now clock is 4Mhz

// Set ADC sampling time for channel 16 (Temperature sensor) in SMPR1 register

// While in SMPR register SMPx must be chosen for appropriate channel

// e.g. SMPR1_SMP10 for Channel 10 SMPR2_SMP0 for Channel 0

 

/*// Clear previous bits for channel sampling rate

ADC1->SMPR1 &= ~ADC_SMPR1_SMP16;

// Longest sampling time for channel 16 (Temperature sensor)

ADC1->SMPR1 |= (ADC_SMPR1_SMP16_2 | ADC_SMPR1_SMP16_1 | ADC_SMPR1_SMP16_0);*/

 

// ENSURE ADC IS ON for CAL for 2cycles!

ADC1 -> CR2 |= (ADC_CR2_ADON | ADC_CR2_CONT); //this is = 1 cycle

//wait for ADC to turn on!

while((ADC1-> CR2 & ADC_CR2_ADON) == 0){} //this is = 2 cycles!

//It should be on by now

 

//TRYING CAL AGAIN...HMMM THIS SEEMS TO WORK

ADC1-> CR2 |= ADC_CR2_CAL;

//ADC1-> CR2 |= ADC_CR2_CAL;

 

LCDSendAString("ADC CALing", 1, 1);

 

while((ADC1 -> CR2 & ADC_CR2_CAL) != 0){}

//Wait to finish CAL

 

LCDSendAString("ADC CALd", 1, 1);

 

ADC1->SMPR2 &= ~ADC_SMPR2_SMP7;

// Longest sampling time for channel 7 (Voltage Divider)

ADC1->SMPR2 |= (ADC_SMPR2_SMP7_2 | ADC_SMPR2_SMP7_1 | ADC_SMPR2_SMP7_0);

 

/*//Turn on channel 16

//10000 is binary for decimal 16 so for channel 16 set the 5th bit of 4:0

ADC1->SQR3 |= ADC_SQR3_SQ1_4;

//Turn on Temperature Sensor and Voltage Reference this could be set at the same

//time as ADON to minimize delay

ADC1->CR2 |= ADC_CR2_TSVREFE;*/

 

//Align the data before beginning the conversion (reSETTING THE BIT IS right ALIGN)

ADC1->CR2 &= ~(ADC_CR2_ALIGN);

 

//Clear previous bits

ADC1->SQR3 &= ~(ADC_SQR3_SQ1);

//Turn on Channel 7 in binary 00111

ADC1->SQR3 |= (ADC_SQR3_SQ1_2 | ADC_SQR3_SQ1_1 | ADC_SQR3_SQ1_0);

/*//Write a message saying everything is ready

LCDSendAString(" Cel", 1, 1);

LCDSendAString("ADC", 6, 2);

// Cal'd 25C V=1.42 so 1.42/3.3V = 1762/4095 ADC values

float V25 = 1762.0000;

// in mV/degreeC per reference manual

float AVG_SLOPE = 0.0043; // 43mV/dC

// .0043/3.3 = 5.3359/4095

float AVGSlopeConvertedToADC = (AVG_SLOPE/3.3)*4095;*/

 

LCDSendAString("Trim Val", 1, 1);

 

 

while (1)

{

//Start the ADC conversion continiuously

ADC1->CR2 |= ADC_CR2_SWSTART;

//Wait for end of conversion flag

while((ADC1->SR & ADC_SR_EOC) == 0){}

//Display the ADC Data Register on the LCD

float ADCData = ADC1->DR;

/*

//Renamed to inputVoltage to match stm32 reference manual

float inputVoltage = ADCData;

float temperature = (((V25 - inputVoltage)/AVGSlopeConvertedToADC)+25);

LCDSendAFloat(temperature, 4, 1, 1);*/

 

//Remove artifacts

LCDSendAString(" ", 1, 2);

LCDSendAnInteger(ADCData, 4, 1, 2);

//set a delay so there the LCD isn't a blur

nonExactTimeDelay(1000000);

}

 

}