stm32f103 bare metal programming issue - code only works if I set ADC_CAL twice in a row!
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 */
