Import YEEP-144 library 1.2 sources
Source-only snapshot of the 1.2 release. Drivers/ (the vendored ST HAL and CMSIS) is excluded at 173 MB; fetch it from STMicroelectronics/STM32CubeH5 to build.
This commit is contained in:
166
YEEP144lib/src/YEEP144.c
Normal file
166
YEEP144lib/src/YEEP144.c
Normal file
@@ -0,0 +1,166 @@
|
||||
#include "YEEP144.h"
|
||||
#include "stm32h563xx.h"
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_gpio.h"
|
||||
#include "stm32h5xx_hal_rcc.h"
|
||||
#include "stm32h5xx_hal_tim.h"
|
||||
|
||||
// Private Function Prototypes
|
||||
void SystemClock_Config(void);
|
||||
void GPIO_Init(void);
|
||||
|
||||
#define SYSTICK_LOAD (SystemCoreClock/1000000U)
|
||||
#define SYSTICK_DELAY_CALIB (85)// Determined experimentally
|
||||
|
||||
/**
|
||||
* This function initialises the minimum required for basic operation
|
||||
* Each feature on top of this will be initialised by its own init function
|
||||
*/
|
||||
int YEEP144_Init(void){
|
||||
HAL_Init();
|
||||
|
||||
SystemClock_Config();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the specified GPIO with basic configuration. This assumes it will be one of the standard modes
|
||||
* with no pull-ups or pull-downs, and a low frequency output
|
||||
*/
|
||||
void YEEP144_GPIO_Init(GPIO_TypeDef* port, uint32_t pins, uint32_t mode){
|
||||
// Initialise GPIO pins
|
||||
|
||||
// Create structure to hold GPIO init data
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
|
||||
GPIO_InitStruct.Pin = pins; // Pins used
|
||||
GPIO_InitStruct.Mode = mode; // Set pin mode
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL; // No pull-ups or pull-downs
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // Low speed is sufficient for this task
|
||||
|
||||
// Initialise the port
|
||||
HAL_GPIO_Init(port, &GPIO_InitStruct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the specified GPIO with access to all of the configuration options, allowing you to configure
|
||||
* pull-ups / pull-downs, GPIO speed, and access alternate functions such as timer IO, SPI, I2C etc
|
||||
*/
|
||||
void YEEP144_GPIO_InitAdv(GPIO_TypeDef* port, uint32_t pins, uint32_t mode, uint32_t pull, uint32_t speed, uint32_t alternate){
|
||||
// Initialise GPIO pins
|
||||
|
||||
// Create structure to hold GPIO init data
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
|
||||
GPIO_InitStruct.Pin = pins; // Pins used
|
||||
GPIO_InitStruct.Mode = mode; // Set pin mode
|
||||
GPIO_InitStruct.Pull = pull; // Set pullup / pulldown
|
||||
GPIO_InitStruct.Speed = speed; // Set speed - low is good for up to 8MHz
|
||||
GPIO_InitStruct.Alternate = alternate; // Set alternate mode
|
||||
|
||||
// Initialise the port
|
||||
HAL_GPIO_Init(port, &GPIO_InitStruct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises and configures the system clocks for standard operation
|
||||
*
|
||||
* By default we are configured for the maximum clock speed of 250MHz on the core
|
||||
* and on the APBx peripheral and timer clocks
|
||||
*
|
||||
* As we do not have a HSE clock due to the configuration of the Nucleo board
|
||||
* all clocks are based from the internal oscillators, primarily the HSI clock
|
||||
* where possible as it should give use the most stable clock from the options available
|
||||
*
|
||||
* This code is mostly copied from an MX generated project
|
||||
*/
|
||||
void SystemClock_Config(void){
|
||||
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
||||
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
|
||||
|
||||
/** Configure the main internal regulator output voltage
|
||||
*/
|
||||
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
|
||||
|
||||
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
|
||||
|
||||
/** Initializes the RCC Oscillators according to the specified parameters
|
||||
* in the RCC_OscInitTypeDef structure.
|
||||
*/
|
||||
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_CSI;
|
||||
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
|
||||
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
|
||||
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
|
||||
RCC_OscInitStruct.CSIState = RCC_CSI_ON;
|
||||
RCC_OscInitStruct.CSICalibrationValue = RCC_CSICALIBRATION_DEFAULT;
|
||||
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
|
||||
RCC_OscInitStruct.PLL.PLLSource = RCC_PLL1_SOURCE_HSI;
|
||||
RCC_OscInitStruct.PLL.PLLM = 32;
|
||||
RCC_OscInitStruct.PLL.PLLN = 250;
|
||||
RCC_OscInitStruct.PLL.PLLP = 2;
|
||||
RCC_OscInitStruct.PLL.PLLQ = 2;
|
||||
RCC_OscInitStruct.PLL.PLLR = 2;
|
||||
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1_VCIRANGE_1;
|
||||
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
|
||||
RCC_OscInitStruct.PLL.PLLFRACN = 0;
|
||||
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_CLOCKTYPE_PCLK3;
|
||||
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
|
||||
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
|
||||
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
|
||||
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
|
||||
RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;
|
||||
|
||||
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
|
||||
{
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
/** Configure the programming delay
|
||||
*/
|
||||
__HAL_FLASH_SET_PROGRAM_DELAY(FLASH_PROGRAMMING_DELAY_2);
|
||||
|
||||
// Enable all GPIO clocks
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOD_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOE_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOF_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOG_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOH_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOI_CLK_ENABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function provides a delay in uS instead of ms
|
||||
* It should only be used for small delays <1ms
|
||||
*/
|
||||
void delayUS(uint32_t us){
|
||||
uint32_t startCount = SysTick->VAL;
|
||||
uint32_t targetCount = startCount - (us * SYSTICK_LOAD) + SYSTICK_DELAY_CALIB;
|
||||
|
||||
// Handle wraparounds
|
||||
// If the targetCount is greater than the LOAD value, this means the previous subtraction
|
||||
// has underflowed
|
||||
if(targetCount > SysTick->LOAD){
|
||||
// Calculate a new targetCount taking the wraparound into account
|
||||
targetCount = SysTick->LOAD - (us * SYSTICK_LOAD) + startCount + (SYSTICK_DELAY_CALIB);
|
||||
|
||||
// Wait until the SysTick reloads
|
||||
while(SysTick->VAL < startCount);
|
||||
startCount = SysTick->LOAD;
|
||||
}
|
||||
|
||||
// Wait until we count down until the targetCount
|
||||
while((SysTick->VAL > targetCount) && (SysTick->VAL < startCount));
|
||||
}
|
||||
126
YEEP144lib/src/YEEP144_ADCPot.c
Normal file
126
YEEP144lib/src/YEEP144_ADCPot.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_Pinout.h"
|
||||
#include "YEEP144_ADCPot.h"
|
||||
|
||||
#include "stm32h5xx_hal_adc.h"
|
||||
#include "stm32h5xx_hal_gpio.h"
|
||||
|
||||
#define ADC_TIMEOUT 1000
|
||||
|
||||
ADC_HandleTypeDef hadc2;
|
||||
|
||||
/**
|
||||
* Initialise the potentiometers. This consumes ADC2, along with 2 GPIO
|
||||
*/
|
||||
void YEEP144_ADCPot_Init(void){
|
||||
|
||||
// Setup and enable the ADC peripheral clock
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
||||
|
||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADCDAC;
|
||||
PeriphClkInitStruct.AdcDacClockSelection = RCC_ADCDACCLKSOURCE_HSI; // HSI = 64MHz input clock
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
__HAL_RCC_ADC_CLK_ENABLE();
|
||||
|
||||
// Configure and initialise the ADC peripheral
|
||||
|
||||
hadc2.Instance = ADC2;
|
||||
hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; // No clock division = 64MHz
|
||||
hadc2.Init.Resolution = ADC_RESOLUTION_12B; // 12-bit resolution (maximum)
|
||||
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
|
||||
hadc2.Init.ScanConvMode = ADC_SCAN_ENABLE;
|
||||
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
|
||||
hadc2.Init.LowPowerAutoWait = DISABLE;
|
||||
hadc2.Init.ContinuousConvMode = DISABLE;
|
||||
hadc2.Init.NbrOfConversion = 2; // 2 channels in use
|
||||
hadc2.Init.DiscontinuousConvMode = DISABLE;
|
||||
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START; // Conversion triggered by software
|
||||
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
|
||||
hadc2.Init.DMAContinuousRequests = DISABLE; // No DMA
|
||||
hadc2.Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL;
|
||||
hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED;
|
||||
hadc2.Init.OversamplingMode = ENABLE; // Enable oversampling
|
||||
hadc2.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_256; // 256x oversampling gets us 4 extra bits of resolution
|
||||
hadc2.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_8; // Shift by 8 to get us back to 12-bit output
|
||||
hadc2.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
|
||||
hadc2.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_CONTINUED_MODE;
|
||||
if (HAL_ADC_Init(&hadc2) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Initialise the GPIO used
|
||||
YEEP144_GPIO_Init(POT1_PORT, POT1_PIN | POT2_PIN, GPIO_MODE_ANALOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value of potentiometer 1.
|
||||
* Returns a 12-bit value in the range 0 - 4095.
|
||||
* This blocks until the conversion is complete, ~1ms
|
||||
*/
|
||||
uint16_t YEEP144_ADCPot_GetPot1(void){
|
||||
|
||||
// Configure this channel to be converted next
|
||||
ADC_ChannelConfTypeDef sConfig = {0};
|
||||
sConfig.Channel = ADC_CHANNEL_2;
|
||||
sConfig.Rank = ADC_REGULAR_RANK_1;
|
||||
sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
|
||||
// With a 64MHz clock and 256x oversampling, 247.5 cycles sample gives us a conversion time of ~1ms
|
||||
// or a maximum sample rate of ~1kHz
|
||||
sConfig.SingleDiff = ADC_SINGLE_ENDED;
|
||||
sConfig.OffsetNumber = ADC_OFFSET_NONE;
|
||||
sConfig.Offset = 0;
|
||||
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Start the conversion
|
||||
HAL_ADC_Start(&hadc2);
|
||||
|
||||
// Wait until the conversion is finished
|
||||
HAL_ADC_PollForConversion(&hadc2, ADC_TIMEOUT);
|
||||
|
||||
// Grab the converted value now conversion is finished
|
||||
uint16_t adcval = HAL_ADC_GetValue(&hadc2);
|
||||
|
||||
// Stop the ADC
|
||||
HAL_ADC_Stop(&hadc2);
|
||||
|
||||
return adcval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value of potentiometer 2.
|
||||
* Returns a 12-bit value in the range 0 - 4095.
|
||||
* This blocks until the conversion is complete, ~1ms
|
||||
*/
|
||||
uint16_t YEEP144_ADCPot_GetPot2(void){
|
||||
ADC_ChannelConfTypeDef sConfig = {0};
|
||||
sConfig.Channel = ADC_CHANNEL_6;
|
||||
sConfig.Rank = ADC_REGULAR_RANK_1;
|
||||
sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
|
||||
// With a 64MHz clock and 256x oversampling, 247.5 cycles sample gives us a conversion time of ~1ms
|
||||
// or a maximum sample rate of ~1kHz
|
||||
sConfig.SingleDiff = ADC_SINGLE_ENDED;
|
||||
sConfig.OffsetNumber = ADC_OFFSET_NONE;
|
||||
sConfig.Offset = 0;
|
||||
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Start the conversion
|
||||
HAL_ADC_Start(&hadc2);
|
||||
|
||||
// Wait until the conversion is finished
|
||||
HAL_ADC_PollForConversion(&hadc2, ADC_TIMEOUT);
|
||||
|
||||
// Grab the converted value now conversion is finished
|
||||
uint16_t adcval = HAL_ADC_GetValue(&hadc2);
|
||||
|
||||
// Stop the ADC
|
||||
HAL_ADC_Stop(&hadc2);
|
||||
|
||||
return adcval;
|
||||
}
|
||||
257
YEEP144lib/src/YEEP144_BluetoothUART.c
Normal file
257
YEEP144lib/src/YEEP144_BluetoothUART.c
Normal file
@@ -0,0 +1,257 @@
|
||||
// YEEP144 includes
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_BluetoothUART.h"
|
||||
#include "YEEP144_Pinout.h"
|
||||
|
||||
// HAL includes
|
||||
#include "stm32h563xx.h"
|
||||
#include "stm32h5xx.h"
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_cortex.h"
|
||||
#include "stm32h5xx_hal_dma.h"
|
||||
#include "stm32h5xx_hal_gpio_ex.h"
|
||||
#include "stm32h5xx_hal_rcc.h"
|
||||
#include "stm32h5xx_hal_uart.h"
|
||||
#include "stm32h5xx_hal_uart_ex.h"
|
||||
|
||||
// Std includes
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define UART_POLL_TIMEOUT 1000
|
||||
|
||||
// Structure for UART info
|
||||
UART_HandleTypeDef huart2;
|
||||
|
||||
DMA_HandleTypeDef handle_GPDMA1_Channel0;
|
||||
|
||||
/**
|
||||
* Initialises USART2 for use with the Bluetooth Serial addon board
|
||||
*
|
||||
* This consumes the USART2 peripheral and all of the UART pins on the comms headers
|
||||
* as well as Channel 0 on GPDMA1
|
||||
*/
|
||||
void YEEP144_BluetoothUART_Init(void){
|
||||
|
||||
// DMA clock and interrupt
|
||||
__HAL_RCC_GPDMA1_CLK_ENABLE();
|
||||
HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
|
||||
HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);
|
||||
|
||||
// Initialise and start the clock
|
||||
|
||||
// Structure to hold clock config
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
||||
|
||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART2; // Configuring USART2
|
||||
PeriphClkInitStruct.Lptim3ClockSelection = RCC_USART2CLKSOURCE_PCLK1; // Select PCLK1
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Enable the clock
|
||||
__HAL_RCC_USART2_CLK_ENABLE();
|
||||
|
||||
// Populate the UART struct with the configuration data
|
||||
// 115200 8-N-1 with flow control
|
||||
huart2.Instance = USART2;
|
||||
huart2.Init.BaudRate = 115200; // 115200 Baud rate
|
||||
huart2.Init.WordLength = UART_WORDLENGTH_8B; // 8 bit
|
||||
huart2.Init.Parity = UART_PARITY_NONE; // None parity
|
||||
huart2.Init.StopBits = UART_STOPBITS_1; // 1 Stop bit
|
||||
huart2.Init.Mode = UART_MODE_TX_RX; // Full duplex
|
||||
huart2.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS; // Full flow control (RTS, CTS)
|
||||
huart2.Init.OverSampling = UART_OVERSAMPLING_8;
|
||||
huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
|
||||
if(HAL_UART_Init(&huart2) != HAL_OK){
|
||||
|
||||
}
|
||||
|
||||
// Initialise GPIO pins
|
||||
YEEP144_GPIO_InitAdv(UART2_TX_PORT, UART2_RX_PIN | UART2_TX_PIN | UART2_CTS_PIN | UART2_RTS_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF7_USART2);
|
||||
|
||||
// Enable USART IRQs
|
||||
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
|
||||
HAL_NVIC_EnableIRQ(USART2_IRQn);
|
||||
|
||||
HAL_UARTEx_DisableFifoMode(&huart2);
|
||||
|
||||
// Populate DMA init structure
|
||||
handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
|
||||
handle_GPDMA1_Channel0.Init.Request = GPDMA1_REQUEST_USART2_RX;
|
||||
handle_GPDMA1_Channel0.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
|
||||
handle_GPDMA1_Channel0.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
handle_GPDMA1_Channel0.Init.SrcInc = DMA_SINC_FIXED;
|
||||
handle_GPDMA1_Channel0.Init.DestInc = DMA_DINC_INCREMENTED;
|
||||
handle_GPDMA1_Channel0.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
|
||||
handle_GPDMA1_Channel0.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
|
||||
handle_GPDMA1_Channel0.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
|
||||
handle_GPDMA1_Channel0.Init.SrcBurstLength = 1;
|
||||
handle_GPDMA1_Channel0.Init.DestBurstLength = 1;
|
||||
handle_GPDMA1_Channel0.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
|
||||
handle_GPDMA1_Channel0.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
|
||||
handle_GPDMA1_Channel0.Init.Mode = DMA_NORMAL;
|
||||
if (HAL_DMA_Init(&handle_GPDMA1_Channel0) != HAL_OK)
|
||||
{
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
__HAL_LINKDMA(&huart2, hdmarx, handle_GPDMA1_Channel0);
|
||||
|
||||
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0, DMA_CHANNEL_NPRIV) != HAL_OK)
|
||||
{
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Set default character match to carriage return
|
||||
YEEP144_BluetoothUART_SetCharMatch('\n');
|
||||
|
||||
// Disable buffering on stdin. This prevents problems when using scanf with the UART redirection
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints to the Bluetooth UART, regardless of what the default UART is set to.
|
||||
* Arguments and returns are the same as the default printf
|
||||
*
|
||||
* Internally this doesn't work using a stream like default printf, but uses a buffer, so the command may
|
||||
* fail if the buffer cannot be allocated, or the output may be truncated if expanding the format specifiers causes
|
||||
* the string to expand by more than 256 bytes
|
||||
*/
|
||||
int YEEP144_BluetoothUART_printf(const char * format, ...){
|
||||
// Calculate how long the input string is, plus extra space for substitition in the format specifiers
|
||||
size_t bufSize = strlen(format) + 256;
|
||||
|
||||
// Try to allocate that buffer
|
||||
char* buffer = malloc(bufSize);
|
||||
|
||||
// If the buffer cannot be allocated, return -1
|
||||
if (buffer == NULL) return -1;
|
||||
|
||||
// Get the pointer to the variadic args
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
|
||||
// Create our final string in the buffer using vsnprintf, passing through our argument pointer
|
||||
vsnprintf(buffer, bufSize, format, args);
|
||||
|
||||
// Calulate the final size of the string
|
||||
bufSize = strlen(buffer);
|
||||
|
||||
// Transmit over UART
|
||||
HAL_UART_Transmit(&huart2, (uint8_t*)buffer, bufSize, UART_POLL_TIMEOUT);
|
||||
|
||||
// Free the memory used
|
||||
free(buffer);
|
||||
|
||||
// Return number of characters printed
|
||||
return bufSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the character match IRQ. When the configured character received an interrupt is fired.
|
||||
* The current transfer will be terminated and the YEEP144_BluetoothUART_MatchCallback will be called
|
||||
*/
|
||||
void YEEP144_BluetoothUART_EnableCharMatchIRQ(void){
|
||||
__HAL_UART_ENABLE_IT(&huart2, UART_IT_CM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the character match IRQ
|
||||
*/
|
||||
void YEEP144_BluetoothUART_DisableCharMatchIRQ(void){
|
||||
__HAL_UART_DISABLE_IT(&huart2, UART_IT_CM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the character match IRQ character. When the configured character received an interrupt is fired.
|
||||
* The current transfer will be terminated and the YEEP144_BluetoothUART_MatchCallback will be called
|
||||
*/
|
||||
void YEEP144_BluetoothUART_SetCharMatch(char match){
|
||||
__HAL_UART_DISABLE(&huart2);
|
||||
MODIFY_REG(huart2.Instance->CR2, USART_CR2_ADD_Msk, match<< USART_CR2_ADD_Pos);
|
||||
__HAL_UART_ENABLE(&huart2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pre and post delimiters for the BT status messages
|
||||
* Setting both to null disables the status messages entirely
|
||||
*/
|
||||
void YEEP144_BluetoothUART_SetStatusDelimiter(char prefix, char postfix){
|
||||
|
||||
// Save this register so we can restore character match IRQ if needed
|
||||
uint32_t config = READ_REG(huart2.Instance->CR1);
|
||||
|
||||
// Disable the character match irq so it doesn't fire during this function
|
||||
YEEP144_BluetoothUART_DisableCharMatchIRQ();
|
||||
|
||||
uint8_t rxBuf[10];
|
||||
uint16_t rxSize;
|
||||
|
||||
// Enter command mode
|
||||
HAL_UART_Transmit(&huart2, "$$$", 3, UART_POLL_TIMEOUT);
|
||||
|
||||
// Wait to receive data back from BT module
|
||||
HAL_UARTEx_ReceiveToIdle(&huart2, rxBuf, 10, &rxSize, UART_POLL_TIMEOUT);
|
||||
|
||||
// Set delimiters
|
||||
char txBuf[8] = {0};
|
||||
if(prefix == '\0' && postfix == '\0'){
|
||||
sprintf(txBuf, "SO,,%c",'\r');
|
||||
}
|
||||
else if(prefix == '\0'){
|
||||
sprintf(txBuf, "SO,,%c%c", postfix, '\r');
|
||||
}
|
||||
else if(postfix == '\0'){
|
||||
sprintf(txBuf, "SO,%c,%c", prefix, '\r');
|
||||
}
|
||||
else{
|
||||
sprintf(txBuf, "SO,%c,%c%c", prefix, postfix, '\r');
|
||||
}
|
||||
HAL_UART_Transmit(&huart2, txBuf, sizeof(rxBuf), UART_POLL_TIMEOUT);
|
||||
|
||||
// Wait to receive data back from BT module
|
||||
HAL_UARTEx_ReceiveToIdle(&huart2, rxBuf, 10, &rxSize, UART_POLL_TIMEOUT);
|
||||
|
||||
// Reboot device to make changes
|
||||
HAL_UART_Transmit(&huart2, "R,1", 3, UART_POLL_TIMEOUT);
|
||||
|
||||
// Wait to receive data back from BT module
|
||||
HAL_UARTEx_ReceiveToIdle(&huart2, rxBuf, 10, &rxSize, UART_POLL_TIMEOUT);
|
||||
|
||||
// Re-enable the character match IRQ if it previously was
|
||||
if(config & USART_CR1_CMIE){
|
||||
YEEP144_BluetoothUART_EnableCharMatchIRQ();
|
||||
}
|
||||
}
|
||||
|
||||
#if UARTSEL == BLUETOOTHUART
|
||||
/**
|
||||
* This function overrides a function from the stdio library, effectively redirecting
|
||||
* output to the UART port. This means that printf can print directly to the UART.
|
||||
*/
|
||||
int __io_putchar(int ch){
|
||||
HAL_UART_Transmit(&huart2, (uint8_t *) &ch, 1, UART_POLL_TIMEOUT);
|
||||
return ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function overrides a function from the stdio library, effectively redirecting
|
||||
* input from the UART port. This means that scanf can read directly from the UART.
|
||||
*/
|
||||
int __io_getchar(void){
|
||||
uint8_t ch = 0;
|
||||
|
||||
/* Clear the Overrun flag just before receiving the first character */
|
||||
__HAL_UART_CLEAR_OREFLAG(&huart2);
|
||||
|
||||
/* Wait for reception of a character on the USART RX line and echo this
|
||||
* character on console */
|
||||
HAL_UART_Receive(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
|
||||
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
|
||||
return ch;
|
||||
}
|
||||
#endif
|
||||
135
YEEP144lib/src/YEEP144_Buzzer.c
Normal file
135
YEEP144lib/src/YEEP144_Buzzer.c
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_Pinout.h"
|
||||
#include "YEEP144_Buzzer.h"
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_gpio.h"
|
||||
#include "stm32h5xx_hal_gpio_ex.h"
|
||||
#include "stm32h5xx_hal_tim.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
TIM_HandleTypeDef htim17buzzer;
|
||||
|
||||
/**
|
||||
* Initialises the buzzer. This consumes timer 17 and 1 GPIO. This resource is shared with servo 4 - only 1 should be initialised
|
||||
* Ensure DIP switch SW5 is correctly set to enable the buzzer output. Set both to off when using servo 4 to avoid hearing
|
||||
* the servo PWM signal through the buzzer
|
||||
*/
|
||||
void YEEP144_Buzzer_Init(void){
|
||||
// Initialise TIM17
|
||||
|
||||
// Create structures to store our timer configuration information
|
||||
TIM_OC_InitTypeDef sConfigOC = {0};
|
||||
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
|
||||
|
||||
// Enable the clock input to TIM3
|
||||
__HAL_RCC_TIM17_CLK_ENABLE();
|
||||
|
||||
// Setup our configuration data
|
||||
// Prescaler is set so that 1 timer tick = 1us for 250MHz input clock, as the frequency calculation assumes this
|
||||
htim17buzzer.Instance = TIM17;
|
||||
htim17buzzer.Init.Prescaler = 249;
|
||||
htim17buzzer.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim17buzzer.Init.Period = 65535;
|
||||
htim17buzzer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
htim17buzzer.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
|
||||
|
||||
// Initialise the timer
|
||||
if (HAL_TIM_PWM_Init(&htim17buzzer) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Configure the Output Compare (OC) settings
|
||||
// This determines how the PWM signal is generated based on the count of our timer
|
||||
sConfigOC.OCMode = TIM_OCMODE_PWM1; // Edge-Aligned mode
|
||||
sConfigOC.Pulse = 0; // Default CCR value for our duty cycle
|
||||
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // Non-inverted polarity
|
||||
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
|
||||
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
|
||||
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
|
||||
|
||||
// Configure the PWM channels
|
||||
if (HAL_TIM_PWM_ConfigChannel(&htim17buzzer, &sConfigOC, TIM_CHANNEL_1) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Disable the break dead time feature as its not needed
|
||||
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
|
||||
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
|
||||
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
|
||||
sBreakDeadTimeConfig.DeadTime = 0;
|
||||
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
|
||||
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
|
||||
sBreakDeadTimeConfig.BreakFilter = 0;
|
||||
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
|
||||
if (HAL_TIMEx_ConfigBreakDeadTime(&htim17buzzer, &sBreakDeadTimeConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Setup the GPIO output from the timer
|
||||
YEEP144_GPIO_InitAdv(BUZZER_PORT, BUZZER_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF1_TIM17);
|
||||
|
||||
// Start PWM generation
|
||||
HAL_TIM_PWM_Start(&htim17buzzer, TIM_CHANNEL_1);
|
||||
|
||||
// Default to no tone
|
||||
YEEP144_Buzzer_NoTone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sound the buzzer at the chosen frequency (Hz) for the alloted duration (ms)
|
||||
* This is blocking as it uses HAL_Delay to wait the alloted time
|
||||
*/
|
||||
void YEEP144_Buzzer_Tone(uint32_t freq, uint32_t duration){
|
||||
YEEP144_Buzzer_GoTone(freq); // Start playing the tone
|
||||
HAL_Delay(duration); // Wait for tone duration
|
||||
YEEP144_Buzzer_NoTone(); // Cease playing the tone
|
||||
}
|
||||
|
||||
/**
|
||||
* Sound the buzzer at the chosen frequency (Hz) continuously until stopped by NoTone
|
||||
* This is non-blocking, so you can go do other things while the tone is playing
|
||||
*/
|
||||
void YEEP144_Buzzer_GoTone(uint32_t freq){
|
||||
if(freq != 0){
|
||||
TIM17->ARR = (1000000UL / freq) - 1; // Set PWM frequency
|
||||
TIM17->CCR1 = (TIM17->ARR >> 1); // Set the duty cycle to 50% by setting Capture Compare Register to half the frequency
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the buzzer from sounding by setting the duty cycle to 0%
|
||||
*/
|
||||
void YEEP144_Buzzer_NoTone(void){
|
||||
TIM17->CCR1 = 0; // Set duty cycle to 0%
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Buzzer rendition of Daisy Bell taken from:
|
||||
* https://github.com/ayoungblood/daisy-bell/blob/master/arduino/daisy_basic/daisy_basic.ino
|
||||
*/
|
||||
#define A440 440 // frequency constant
|
||||
#define QUARTER 180 // quarter note length in ms
|
||||
|
||||
const uint8_t DAISY_P[] = {
|
||||
74, 71, 67, 62, 64, 66, 67, 64, 67, 62,
|
||||
69, 74, 71, 67, 64, 66, 67, 69, 71, 69,
|
||||
71, 72, 71, 69, 74, 71, 69, 67, 69, 71, 67, 64, 67, 64, 62,
|
||||
62, 67, 71, 69, 67, 71, 69, 71, 72, 74, 71, 67, 69, 62, 67
|
||||
};
|
||||
const uint8_t DAISY_T[] = {
|
||||
3, 3, 3, 3, 1, 1, 1, 2, 1, 6,
|
||||
3, 3, 3, 3, 1, 1, 1, 2, 1, 6,
|
||||
1, 1, 1, 1, 2, 1, 1, 4, 1, 2, 1, 2, 1, 1, 5,
|
||||
1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 5
|
||||
};
|
||||
|
||||
void YEEP144_Buzzer_Daisy(void){
|
||||
for(int i=0; i<50; ++i){
|
||||
float freq = pow(2,(DAISY_P[i]-69)/12.0f)*A440;
|
||||
YEEP144_Buzzer_Tone((uint32_t)freq, DAISY_T[i]*QUARTER);
|
||||
}
|
||||
}
|
||||
386
YEEP144lib/src/YEEP144_DCMotor.c
Normal file
386
YEEP144lib/src/YEEP144_DCMotor.c
Normal file
@@ -0,0 +1,386 @@
|
||||
// YEEP Includes
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_DCMotor.h"
|
||||
#include "YEEP144_Pinout.h"
|
||||
|
||||
// HAL Includes
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_gpio_ex.h"
|
||||
#include "stm32h5xx_hal_rcc.h"
|
||||
#include "stm32h5xx_hal_tim.h"
|
||||
#include "stm32h5xx_hal_lptim.h"
|
||||
#include "stm32h5xx_hal_gpio.h"
|
||||
|
||||
// System Includes
|
||||
#include <stdint.h>
|
||||
|
||||
// Structs for timer handles
|
||||
// TIM3 used for all 4 DC motors
|
||||
TIM_HandleTypeDef htim3;
|
||||
|
||||
// TIM3, LPTIM2, LPTIM3, LPTIM5 used for encoders
|
||||
TIM_HandleTypeDef htim4;
|
||||
LPTIM_HandleTypeDef hlptim2;
|
||||
LPTIM_HandleTypeDef hlptim3;
|
||||
LPTIM_HandleTypeDef hlptim5;
|
||||
|
||||
/**
|
||||
* Sets the specified motors speed by altering the duty cycle of the PWM signal
|
||||
* This is done by setting the Capture and Compare Register for the relevant channel
|
||||
* to a value between 0 and the Period of the timer where CCR = 0 is 0% duty cycle and
|
||||
* CCR = Period is 100% duty cycle
|
||||
*/
|
||||
void YEEP144_DCMotor_SetSpeed(uint8_t motor, uint16_t speed){
|
||||
// Cap speed if out of bounds
|
||||
if(speed > DCMOTOR_MAX_SPEED){
|
||||
speed = DCMOTOR_MAX_SPEED;
|
||||
}
|
||||
|
||||
// Update the Capture Compare Register (CCR) for the relevant motor channel
|
||||
switch(motor){
|
||||
case DCMOTOR1:
|
||||
TIM3->CCR1 = speed;
|
||||
break;
|
||||
case DCMOTOR2:
|
||||
TIM3->CCR2 = speed;
|
||||
break;
|
||||
case DCMOTOR3:
|
||||
TIM3->CCR3 = speed;
|
||||
break;
|
||||
case DCMOTOR4:
|
||||
TIM3->CCR4 = speed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the two direction bits for the specified motor. There are 4 valid options
|
||||
* for stopping, counter-clockwise movement, clockwise movement, and a short
|
||||
* brake. There are #defines available for these states in YEEP144_DCMotor.h
|
||||
*/
|
||||
void YEEP144_DCMotor_SetDirection(uint8_t motor, uint8_t direction){
|
||||
// Check that the inputs are valid
|
||||
if(direction > 3 || motor > 4){
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the direction GPIO pins
|
||||
// Some bit manipulation is used to mask the relevant bit and shift it into the LSB position
|
||||
if(motor == DCMOTOR1){
|
||||
HAL_GPIO_WritePin(MOT1A_PORT, MOT1A_PIN, (direction>>1) & 1);
|
||||
HAL_GPIO_WritePin(MOT1B_PORT, MOT1B_PIN, direction & 1);
|
||||
}
|
||||
else if (motor == DCMOTOR2) {
|
||||
HAL_GPIO_WritePin(MOT2A_PORT, MOT2A_PIN, (direction>>1) & 1);
|
||||
HAL_GPIO_WritePin(MOT2B_PORT, MOT2B_PIN, direction & 1);
|
||||
}
|
||||
else if (motor == DCMOTOR3) {
|
||||
HAL_GPIO_WritePin(MOT3A_PORT, MOT3A_PIN, (direction>>1) & 1);
|
||||
HAL_GPIO_WritePin(MOT3B_PORT, MOT3B_PIN, direction & 1);
|
||||
}
|
||||
else if (motor == DCMOTOR4) {
|
||||
HAL_GPIO_WritePin(MOT4A_PORT, MOT4A_PIN, (direction>>1) & 1);
|
||||
HAL_GPIO_WritePin(MOT4B_PORT, MOT4B_PIN, direction & 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value of encoder 1
|
||||
*/
|
||||
uint16_t YEEP144_Encoder1_Get(void){
|
||||
return TIM4->CNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value of encoder 2
|
||||
*/
|
||||
uint16_t YEEP144_Encoder2_Get(void){
|
||||
return LPTIM3->CNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value of encoder 3
|
||||
*/
|
||||
uint16_t YEEP144_Encoder3_Get(void){
|
||||
return LPTIM5->CNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value of encoder 4
|
||||
*/
|
||||
uint16_t YEEP144_Encoder4_Get(void){
|
||||
return LPTIM2->CNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function initialises all 4 DC motor driver channels
|
||||
* They all share TIM3 for their PWM generation
|
||||
* After initialisation the PWM will be set to 0%, and direction set to STOP
|
||||
*
|
||||
* This consumes TIM3 and the 4 GPIO it outputs on, as well as the 8 GPIO used for motor direction control
|
||||
*/
|
||||
void YEEP144_DCMotor_Init(void){
|
||||
// Initialise TIM3
|
||||
|
||||
// Create structures to store our timer configuration information
|
||||
TIM_MasterConfigTypeDef sMasterConfig = {0};
|
||||
TIM_OC_InitTypeDef sConfigOC = {0};
|
||||
|
||||
// Enable the clock input to TIM3
|
||||
__HAL_RCC_TIM3_CLK_ENABLE();
|
||||
|
||||
// Setup our configuration data
|
||||
// For an input clock of 250MHz and a Period of 4095 these setting will produce
|
||||
// a PWM signal of ~61kHz which is within the range accepted by the motor driver
|
||||
|
||||
// 4095 was chosen as the period as it maps 1-to-1 with the reading from a 12-bit ADC
|
||||
htim3.Instance = TIM3;
|
||||
htim3.Init.Prescaler = 0;
|
||||
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim3.Init.Period = DCMOTOR_MAX_SPEED;
|
||||
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
|
||||
// Initialise the timer
|
||||
if (HAL_TIM_PWM_Init(&htim3) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Configure master output
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Configure the Output Compare (OC) settings
|
||||
// This determines how the PWM signal is generated based on the count of our timer
|
||||
sConfigOC.OCMode = TIM_OCMODE_PWM1; // Edge-Aligned mode
|
||||
sConfigOC.Pulse = 0; // Default CCR value for our duty cycle
|
||||
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // Non-inverted polarity
|
||||
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // Fast mode is not required for this application
|
||||
|
||||
// Configure the PWM channels
|
||||
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_4) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Initialise GPIO pins used for PWM output
|
||||
// 3 of our PWM pins are on port C and 1 on port B
|
||||
// We must configure each port separately
|
||||
|
||||
YEEP144_GPIO_InitAdv(MOT1PWM_PORT, MOT1PWM_PIN | MOT2PWM_PIN | MOT4PWM_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF2_TIM3);
|
||||
YEEP144_GPIO_InitAdv(MOT3PWM_PORT, MOT3PWM_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF2_TIM3);
|
||||
|
||||
// Initialise GPIO for 2-bit direction control
|
||||
// The pins used are split across 3 ports
|
||||
// Each port must be configured separately
|
||||
|
||||
YEEP144_GPIO_Init(MOT1A_PORT, MOT1A_PIN | MOT1B_PIN | MOT2A_PIN | MOT2B_PIN, GPIO_MODE_OUTPUT);
|
||||
YEEP144_GPIO_Init(MOT3A_PORT, MOT3A_PIN | MOT3B_PIN, GPIO_MODE_OUTPUT);
|
||||
YEEP144_GPIO_Init(MOT4A_PORT, MOT4A_PIN | MOT4B_PIN, GPIO_MODE_OUTPUT);
|
||||
|
||||
// Set default outputs to stop
|
||||
YEEP144_DCMotor_SetDirection(DCMOTOR1, DCMOTOR_STOP);
|
||||
YEEP144_DCMotor_SetDirection(DCMOTOR2, DCMOTOR_STOP);
|
||||
YEEP144_DCMotor_SetDirection(DCMOTOR3, DCMOTOR_STOP);
|
||||
YEEP144_DCMotor_SetDirection(DCMOTOR4, DCMOTOR_STOP);
|
||||
|
||||
// Start all the PWM channels
|
||||
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
|
||||
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
|
||||
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
|
||||
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This initialises Encoder 1
|
||||
* This consumes TIM4 and the two GPIO it takes input from
|
||||
*/
|
||||
void YEEP144_Encoder1_Init(void){
|
||||
// Initialise TIM4
|
||||
|
||||
// Enable TIM4 clock
|
||||
__HAL_RCC_TIM4_CLK_ENABLE();
|
||||
|
||||
// Create structs to hold config data
|
||||
TIM_Encoder_InitTypeDef sConfig = {0};
|
||||
TIM_MasterConfigTypeDef sMasterConfig = {0};
|
||||
|
||||
htim4.Instance = TIM4;
|
||||
htim4.Init.Prescaler = 0;
|
||||
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim4.Init.Period = 65535;
|
||||
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
|
||||
// Configure encoder mode and input channels
|
||||
sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
|
||||
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
|
||||
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
|
||||
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
|
||||
sConfig.IC1Filter = 0;
|
||||
sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
|
||||
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
|
||||
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
|
||||
sConfig.IC2Filter = 0;
|
||||
|
||||
// Initialise the timer
|
||||
if (HAL_TIM_Encoder_Init(&htim4, &sConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Initialise the GPIO used for the encoder input
|
||||
YEEP144_GPIO_InitAdv(ENC1A_PORT, ENC1A_PIN | ENC1B_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF2_TIM4);
|
||||
|
||||
// Start the encoder counting
|
||||
HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This initialises Encoder 2
|
||||
* This consumes LPTIM3 and the two GPIO it takes input from
|
||||
*/
|
||||
void YEEP144_Encoder2_Init(void){
|
||||
|
||||
// Setup clock for LPTIM3
|
||||
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
||||
|
||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM3;
|
||||
PeriphClkInitStruct.Lptim3ClockSelection = RCC_LPTIM3CLKSOURCE_PCLK3;
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Enable clock for LPTIM3
|
||||
__HAL_RCC_LPTIM3_CLK_ENABLE();
|
||||
|
||||
// Configure LPTIM3 for encoder input mode
|
||||
hlptim3.Instance = LPTIM3;
|
||||
hlptim3.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
|
||||
hlptim3.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;
|
||||
hlptim3.Init.UltraLowPowerClock.Polarity = LPTIM_CLOCKPOLARITY_RISING;
|
||||
hlptim3.Init.UltraLowPowerClock.SampleTime = LPTIM_CLOCKSAMPLETIME_DIRECTTRANSITION;
|
||||
hlptim3.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
|
||||
hlptim3.Init.Period = 65535;
|
||||
hlptim3.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE;
|
||||
hlptim3.Init.CounterSource = LPTIM_COUNTERSOURCE_EXTERNAL;
|
||||
hlptim3.Init.Input1Source = LPTIM_INPUT1SOURCE_GPIO;
|
||||
hlptim3.Init.Input2Source = LPTIM_INPUT2SOURCE_GPIO;
|
||||
hlptim3.Init.RepetitionCounter = 0;
|
||||
if (HAL_LPTIM_Init(&hlptim3) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Initialise the GPIO used for the encoder input
|
||||
YEEP144_GPIO_InitAdv(ENC2A_PORT, ENC2A_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF2_LPTIM3);
|
||||
YEEP144_GPIO_InitAdv(ENC2B_PORT, ENC2B_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF2_LPTIM3);
|
||||
|
||||
// Start the encoder counting
|
||||
HAL_LPTIM_Encoder_Start(&hlptim3);
|
||||
}
|
||||
|
||||
/**
|
||||
* This initialises Encoder 3
|
||||
* This consumes LPTIM5 and the two GPIO it takes input from
|
||||
*/
|
||||
void YEEP144_Encoder3_Init(void){
|
||||
|
||||
// Setup clock for LPTIM5
|
||||
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
||||
|
||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM5;
|
||||
PeriphClkInitStruct.Lptim3ClockSelection = RCC_LPTIM3CLKSOURCE_PCLK3;
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Enable clock for LPTIM3
|
||||
__HAL_RCC_LPTIM5_CLK_ENABLE();
|
||||
|
||||
// Configure LPTIM5 for encoder input mode
|
||||
hlptim5.Instance = LPTIM5;
|
||||
hlptim5.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
|
||||
hlptim5.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;
|
||||
hlptim5.Init.UltraLowPowerClock.Polarity = LPTIM_CLOCKPOLARITY_RISING;
|
||||
hlptim5.Init.UltraLowPowerClock.SampleTime = LPTIM_CLOCKSAMPLETIME_DIRECTTRANSITION;
|
||||
hlptim5.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
|
||||
hlptim5.Init.Period = 65535;
|
||||
hlptim5.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE;
|
||||
hlptim5.Init.CounterSource = LPTIM_COUNTERSOURCE_EXTERNAL;
|
||||
hlptim5.Init.Input1Source = LPTIM_INPUT1SOURCE_GPIO;
|
||||
hlptim5.Init.Input2Source = LPTIM_INPUT2SOURCE_GPIO;
|
||||
hlptim5.Init.RepetitionCounter = 0;
|
||||
if (HAL_LPTIM_Init(&hlptim5) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Initialise the GPIO used for the encoder input
|
||||
YEEP144_GPIO_InitAdv(ENC3A_PORT, ENC3A_PIN | ENC3B_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF13_LPTIM5);
|
||||
|
||||
// Start the encoder counting
|
||||
HAL_LPTIM_Encoder_Start(&hlptim5);
|
||||
}
|
||||
|
||||
/**
|
||||
* This initialises Encoder 4
|
||||
* This consumes LPTIM2 and the two GPIO it takes input from
|
||||
*/
|
||||
void YEEP144_Encoder4_Init(void){
|
||||
|
||||
// Setup clock for LPTIM2
|
||||
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
||||
|
||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM2;
|
||||
PeriphClkInitStruct.Lptim3ClockSelection = RCC_LPTIM3CLKSOURCE_PCLK3;
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Enable clock for LPTIM2
|
||||
__HAL_RCC_LPTIM2_CLK_ENABLE();
|
||||
|
||||
// Configure LPTIM2 for encoder input mode
|
||||
hlptim2.Instance = LPTIM2;
|
||||
hlptim2.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
|
||||
hlptim2.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;
|
||||
hlptim2.Init.UltraLowPowerClock.Polarity = LPTIM_CLOCKPOLARITY_RISING;
|
||||
hlptim2.Init.UltraLowPowerClock.SampleTime = LPTIM_CLOCKSAMPLETIME_DIRECTTRANSITION;
|
||||
hlptim2.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
|
||||
hlptim2.Init.Period = 65535;
|
||||
hlptim2.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE;
|
||||
hlptim2.Init.CounterSource = LPTIM_COUNTERSOURCE_EXTERNAL;
|
||||
hlptim2.Init.Input1Source = LPTIM_INPUT1SOURCE_GPIO;
|
||||
hlptim2.Init.Input2Source = LPTIM_INPUT2SOURCE_GPIO;
|
||||
hlptim2.Init.RepetitionCounter = 0;
|
||||
if (HAL_LPTIM_Init(&hlptim2) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Initialise the GPIO used for the encoder input
|
||||
YEEP144_GPIO_InitAdv(ENC4A_PORT, ENC4A_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF3_LPTIM2);
|
||||
YEEP144_GPIO_InitAdv(ENC4B_PORT, ENC4B_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF3_LPTIM2);
|
||||
|
||||
// Start the encoder counting
|
||||
HAL_LPTIM_Encoder_Start(&hlptim2);
|
||||
}
|
||||
200
YEEP144lib/src/YEEP144_DebugUART.c
Normal file
200
YEEP144lib/src/YEEP144_DebugUART.c
Normal file
@@ -0,0 +1,200 @@
|
||||
// YEEP144 includes
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_DebugUART.h"
|
||||
#include "YEEP144_Pinout.h"
|
||||
|
||||
// HAL includes
|
||||
#include "stm32h563xx.h"
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_uart.h"
|
||||
|
||||
// Std includes
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define UART_POLL_TIMEOUT 1000
|
||||
|
||||
// Structure for UART info
|
||||
UART_HandleTypeDef huart3;
|
||||
|
||||
DMA_HandleTypeDef handle_GPDMA1_Channel1;
|
||||
|
||||
/**
|
||||
* Initialises USART3 for use as a debug console output via the STLink
|
||||
* Virtual COM Port (VCP)
|
||||
*
|
||||
* This consumes the USART3 peripheral and no YEEP GPIO as it is routed internally
|
||||
* on the Nucleo board and is not broken out
|
||||
*/
|
||||
void YEEP144_DebugUART_Init(void){
|
||||
|
||||
// Initialise and start the clock
|
||||
|
||||
// DMA clock and interrupt
|
||||
__HAL_RCC_GPDMA1_CLK_ENABLE();
|
||||
HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 0, 0);
|
||||
HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
|
||||
|
||||
// Structure to hold clock config
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
||||
|
||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART3; // Configuring USART3
|
||||
PeriphClkInitStruct.Lptim3ClockSelection = RCC_USART3CLKSOURCE_PCLK1; // Select PCLK1
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Enable the clock
|
||||
__HAL_RCC_USART3_CLK_ENABLE();
|
||||
|
||||
// Populate the UART struct with the configuration data
|
||||
// 115200 8-N-1
|
||||
huart3.Instance = USART3;
|
||||
huart3.Init.BaudRate = 115200; // 115200 Baud rate
|
||||
huart3.Init.WordLength = UART_WORDLENGTH_8B; // 8 bit
|
||||
huart3.Init.Parity = UART_PARITY_NONE; // None parity
|
||||
huart3.Init.StopBits = UART_STOPBITS_1; // 1 Stop bit
|
||||
huart3.Init.Mode = UART_MODE_TX_RX; // Full duplex
|
||||
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; // No hardware flow control (RTS, CTS)
|
||||
huart3.Init.OverSampling = UART_OVERSAMPLING_8;
|
||||
huart3.Init.ClockPrescaler = UART_PRESCALER_DIV1;
|
||||
if(HAL_UART_Init(&huart3) != HAL_OK){
|
||||
|
||||
}
|
||||
|
||||
// Initialise GPIO pins
|
||||
YEEP144_GPIO_InitAdv(UART3_TX_PORT, UART3_RX_PIN | UART3_TX_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF7_USART3);
|
||||
|
||||
|
||||
// Enable USART IRQs
|
||||
HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
|
||||
HAL_NVIC_EnableIRQ(USART3_IRQn);
|
||||
|
||||
HAL_UARTEx_DisableFifoMode(&huart3);
|
||||
|
||||
// Populate DMA init structure
|
||||
handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
|
||||
handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_USART3_RX;
|
||||
handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
|
||||
handle_GPDMA1_Channel1.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_FIXED;
|
||||
handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_INCREMENTED;
|
||||
handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
|
||||
handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
|
||||
handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
|
||||
handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
|
||||
handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
|
||||
handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
|
||||
handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
|
||||
handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
|
||||
if (HAL_DMA_Init(&handle_GPDMA1_Channel1) != HAL_OK)
|
||||
{
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
__HAL_LINKDMA(&huart3, hdmarx, handle_GPDMA1_Channel1);
|
||||
|
||||
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV) != HAL_OK)
|
||||
{
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Set default character match to carriage return
|
||||
YEEP144_DebugUART_SetCharMatch('\r');
|
||||
|
||||
// Disable buffering on stdin. This prevents problems when using scanf with the UART redirection
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints to the Debug UART, regardless of what the default UART is set to.
|
||||
* Arguments and returns are the same as the default printf
|
||||
*
|
||||
* Internally this doesn't work using a stream like default printf, but uses a buffer, so the command may
|
||||
* fail if the buffer cannot be allocated, or the output may be truncated if expanding the format specifiers causes
|
||||
* the string to expand by more than 256 bytes
|
||||
*/
|
||||
int YEEP144_DebugUART_printf(const char * format, ...){
|
||||
// Calculate how long the input string is, plus extra space for substitition in the format specifiers
|
||||
size_t bufSize = strlen(format) + 256;
|
||||
|
||||
// Try to allocate that buffer
|
||||
char* buffer = malloc(bufSize);
|
||||
|
||||
// If the buffer cannot be allocated, return -1
|
||||
if (buffer == NULL) return -1;
|
||||
|
||||
// Get the pointer to the variadic args
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
|
||||
// Create our final string in the buffer using vsnprintf, passing through our argument pointer
|
||||
vsnprintf(buffer, bufSize, format, args);
|
||||
|
||||
// Calulate the final size of the string
|
||||
bufSize = strlen(buffer);
|
||||
|
||||
// Transmit over UART
|
||||
HAL_UART_Transmit(&huart3, (uint8_t*)buffer, bufSize, UART_POLL_TIMEOUT);
|
||||
|
||||
// Free the memory used
|
||||
free(buffer);
|
||||
|
||||
// Return number of characters printed
|
||||
return bufSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the character match IRQ. When the configured character received an interrupt is fired.
|
||||
* The current transfer will be terminated and the YEEP144_DebugUART_MatchCallback will be called
|
||||
*/
|
||||
void YEEP144_DebugUART_EnableCharMatchIRQ(void){
|
||||
__HAL_UART_ENABLE_IT(&huart3, UART_IT_CM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the character match IRQ
|
||||
*/
|
||||
void YEEP144_DebugUART_DisableCharMatchIRQ(void){
|
||||
__HAL_UART_DISABLE_IT(&huart3, UART_IT_CM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the character match IRQ character. When the configured character received an interrupt is fired.
|
||||
* The current transfer will be terminated and the YEEP144_DebugUART_MatchCallback will be called
|
||||
*/
|
||||
void YEEP144_DebugUART_SetCharMatch(char match){
|
||||
__HAL_UART_DISABLE(&huart3);
|
||||
MODIFY_REG(huart3.Instance->CR2, USART_CR2_ADD_Msk, match<< USART_CR2_ADD_Pos);
|
||||
__HAL_UART_ENABLE(&huart3);
|
||||
}
|
||||
|
||||
#if UARTSEL == DEBUG
|
||||
/**
|
||||
* This function overrides a function from the stdio library, effectively redirecting
|
||||
* output to the UART port. This means that printf can print directly to the UART.
|
||||
*/
|
||||
int __io_putchar(int ch){
|
||||
HAL_UART_Transmit(&huart3, (uint8_t *) &ch, 1, UART_POLL_TIMEOUT);
|
||||
return ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function overrides a function from the stdio library, effectively redirecting
|
||||
* input from the UART port. This means that scanf can read directly from the UART.
|
||||
*/
|
||||
int __io_getchar(void){
|
||||
uint8_t ch = 0;
|
||||
|
||||
/* Clear the Overrun flag just before receiving the first character */
|
||||
__HAL_UART_CLEAR_OREFLAG(&huart3);
|
||||
|
||||
/* Wait for reception of a character on the USART RX line and echo this
|
||||
* character on console */
|
||||
HAL_UART_Receive(&huart3, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
|
||||
HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
|
||||
return ch;
|
||||
}
|
||||
#endif
|
||||
18
YEEP144lib/src/YEEP144_GP2Y0E02B.c
Normal file
18
YEEP144lib/src/YEEP144_GP2Y0E02B.c
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "YEEP144_GP2Y0E02B.h"
|
||||
#include "YEEP144_I2C.h"
|
||||
#include "stm32h5xx_hal_i2c.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define I2C_TIMEOUT 1000
|
||||
|
||||
float YEEP144_GP2Y0E02B_Read(void){
|
||||
uint8_t IRShift;
|
||||
uint8_t IRValues[2] ={0};
|
||||
|
||||
HAL_I2C_Mem_Read(&hi2c2, GP2Y0E02B_I2C_ADDR, GP2Y0E02B_SHIFT_REG_ADDR, I2C_MEMADD_SIZE_8BIT, &IRShift, 1, I2C_TIMEOUT);
|
||||
HAL_I2C_Mem_Read(&hi2c2, GP2Y0E02B_I2C_ADDR, GP2Y0E02B_DISTANCE_REG_ADDR, I2C_MEMADD_SIZE_8BIT, IRValues, 2, I2C_TIMEOUT);
|
||||
|
||||
return ((IRValues[0]*16.0) + IRValues[1])/ 16.0 / pow(2.0, IRShift);
|
||||
|
||||
}
|
||||
596
YEEP144lib/src/YEEP144_HCSR04.c
Normal file
596
YEEP144lib/src/YEEP144_HCSR04.c
Normal file
@@ -0,0 +1,596 @@
|
||||
#include "YEEP144_HCSR04.h"
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_Pinout.h"
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_cortex.h"
|
||||
#include "stm32h5xx_hal_exti.h"
|
||||
#include "stm32h5xx_hal_gpio.h"
|
||||
#include "stm32h5xx_hal_tim.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define USONIC_PING_LEN 10
|
||||
#define BLOCKING_TIMEOUT_TICKS 1000
|
||||
|
||||
#define USONIC1_MASK (1 << 0)
|
||||
#define USONIC2_MASK (1 << 1)
|
||||
#define USONIC3_MASK (1 << 2)
|
||||
#define USONIC4_MASK (1 << 3)
|
||||
|
||||
uint8_t ultrasonicInit = 0;
|
||||
uint8_t ultrasonicTIMInit = 0;
|
||||
|
||||
uint8_t ultrasonicNextPing = USONIC1_MASK;
|
||||
uint8_t ultrasonicPing = 0;
|
||||
uint8_t ultrasonicEcho = 0;
|
||||
|
||||
uint16_t ultrasonicTickStart[4];
|
||||
uint16_t ultrasonicTickLen[4];
|
||||
|
||||
TIM_HandleTypeDef htim6;
|
||||
|
||||
EXTI_HandleTypeDef hexti12;
|
||||
EXTI_ConfigTypeDef hexti12_conf;
|
||||
|
||||
void YEEP144_Ultrasonic_TIMInit(void);
|
||||
|
||||
void YEEP144_Ultrasonic1_Callback(uint16_t ticks);
|
||||
void YEEP144_Ultrasonic2_Callback(uint16_t ticks);
|
||||
void YEEP144_Ultrasonic3_Callback(uint16_t ticks);
|
||||
void YEEP144_Ultrasonic4_Callback(uint16_t ticks);
|
||||
|
||||
|
||||
/**
|
||||
* Initialises ultrasonic sensor 1. This consumes the two GPIO assigned to that sensor,
|
||||
* TIM6 (shared by all 4 ultrasonics), and EXTI line 10 (share by the first 3 ultrasonics)
|
||||
*/
|
||||
void YEEP144_Ultrasonic1_Init(void){
|
||||
YEEP144_GPIO_Init(USONIC1_TRIG_PORT, USONIC1_TRIG_PIN, GPIO_MODE_OUTPUT);
|
||||
HAL_NVIC_SetPriority(USONIC1_ECHO_EXTI, 1, 0);
|
||||
HAL_NVIC_EnableIRQ(USONIC1_ECHO_EXTI);
|
||||
ultrasonicInit |= USONIC1_MASK;
|
||||
|
||||
// Only initialise the timer if it hasn't already
|
||||
if(ultrasonicTIMInit == 0){
|
||||
YEEP144_Ultrasonic_TIMInit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises ultrasonic sensor 2. This consumes the two GPIO assigned to that sensor,
|
||||
* TIM6 (shared by all 4 ultrasonics), and EXTI line 10 (share by the first 3 ultrasonics)
|
||||
*/
|
||||
void YEEP144_Ultrasonic2_Init(void){
|
||||
YEEP144_GPIO_Init(USONIC2_TRIG_PORT, USONIC2_TRIG_PIN, GPIO_MODE_OUTPUT);
|
||||
HAL_NVIC_SetPriority(USONIC2_ECHO_EXTI, 1, 0);
|
||||
HAL_NVIC_EnableIRQ(USONIC2_ECHO_EXTI);
|
||||
ultrasonicInit |= USONIC2_MASK;
|
||||
|
||||
// Only initialise the timer if it hasn't already
|
||||
if(ultrasonicTIMInit == 0){
|
||||
YEEP144_Ultrasonic_TIMInit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises ultrasonic sensor 3. This consumes the two GPIO assigned to that sensor,
|
||||
* TIM6 (shared by all 4 ultrasonics), and EXTI line 10 (share by the first 3 ultrasonics)
|
||||
*/
|
||||
void YEEP144_Ultrasonic3_Init(void){
|
||||
YEEP144_GPIO_Init(USONIC3_TRIG_PORT, USONIC3_TRIG_PIN, GPIO_MODE_OUTPUT);
|
||||
HAL_NVIC_SetPriority(USONIC3_ECHO_EXTI, 1, 0);
|
||||
HAL_NVIC_EnableIRQ(USONIC4_ECHO_EXTI);
|
||||
ultrasonicInit |= USONIC3_MASK;
|
||||
|
||||
// Only initialise the timer if it hasn't already
|
||||
if(ultrasonicTIMInit == 0){
|
||||
YEEP144_Ultrasonic_TIMInit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises ultrasonic sensor 4. This consumes the two GPIO assigned to that sensor,
|
||||
* TIM6 (shared by all 4 ultrasonics), and EXTI line 12 (shared with button 3 / red)
|
||||
*/
|
||||
void YEEP144_Ultrasonic4_Init(void){
|
||||
YEEP144_GPIO_Init(USONIC4_TRIG_PORT, USONIC4_TRIG_PIN, GPIO_MODE_OUTPUT);
|
||||
HAL_NVIC_SetPriority(USONIC4_ECHO_EXTI, 1, 0);
|
||||
HAL_NVIC_EnableIRQ(USONIC4_ECHO_EXTI);
|
||||
ultrasonicInit |= USONIC4_MASK;
|
||||
|
||||
// Only initialise the timer if it hasn't already
|
||||
if(ultrasonicTIMInit == 0){
|
||||
YEEP144_Ultrasonic_TIMInit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a reading on the next sensor in the sequence using the non-blocking method, skipping over any uninitialised sensors
|
||||
* Only 1 sensor can be read at a time as they (mostly) share the same EXTI line, so this function
|
||||
* checks to make sure that noting else is currently pinging.
|
||||
*
|
||||
* If no sensor is currently initialised returns 255
|
||||
* If a sensor is already pinging, return 0
|
||||
* If a new reading is successfully started, return the status byte indicating which sensor is currently pinging
|
||||
*
|
||||
* The relevant callback function will be called when pinging finishes and a new distance value is available
|
||||
*/
|
||||
uint8_t YEEP144_Ultrasonic_PingNext(void){
|
||||
|
||||
// If nothing is currently pinging
|
||||
if(!ultrasonicPing){
|
||||
// What is the next in the sequence
|
||||
switch(ultrasonicNextPing){
|
||||
case USONIC1_MASK:
|
||||
// Only start if it is enabled
|
||||
if(ultrasonicInit&USONIC1_MASK){
|
||||
// Reinitialise the pin to setup the interrupt
|
||||
YEEP144_GPIO_Init(USONIC1_ECHO_PORT, USONIC1_ECHO_PIN, GPIO_MODE_IT_RISING_FALLING);
|
||||
|
||||
// Toggle the pin on for 10uS
|
||||
HAL_GPIO_WritePin(USONIC1_TRIG_PORT, USONIC1_TRIG_PIN, GPIO_PIN_SET);
|
||||
delayUS(USONIC_PING_LEN);
|
||||
HAL_GPIO_WritePin(USONIC1_TRIG_PORT, USONIC1_TRIG_PIN, GPIO_PIN_RESET);
|
||||
|
||||
// Set the ping bit so we know this sensor is pinging
|
||||
ultrasonicPing |= USONIC1_MASK;
|
||||
|
||||
// Move to next sensor
|
||||
ultrasonicNextPing = USONIC2_MASK;
|
||||
break; // Break is within the if statement, so if a sensor is not initialised it will fall through to the next
|
||||
}
|
||||
else{
|
||||
// Move to next sensor
|
||||
ultrasonicNextPing = USONIC2_MASK;
|
||||
}
|
||||
// fallthrough
|
||||
case USONIC2_MASK:
|
||||
if(ultrasonicInit&USONIC2_MASK){
|
||||
// Reinitialise the pin to setup the interrupt
|
||||
YEEP144_GPIO_Init(USONIC2_ECHO_PORT, USONIC2_ECHO_PIN, GPIO_MODE_IT_RISING_FALLING);
|
||||
|
||||
// Toggle the pin on for 10uS
|
||||
HAL_GPIO_WritePin(USONIC2_TRIG_PORT, USONIC2_TRIG_PIN, GPIO_PIN_SET);
|
||||
delayUS(USONIC_PING_LEN);
|
||||
HAL_GPIO_WritePin(USONIC2_TRIG_PORT, USONIC2_TRIG_PIN, GPIO_PIN_RESET);
|
||||
|
||||
// Set the ping bit so we know this sensor is pinging
|
||||
ultrasonicPing |= USONIC2_MASK;
|
||||
|
||||
// Move to next sensor
|
||||
ultrasonicNextPing = USONIC3_MASK;
|
||||
break; // Break is within the if statement, so if a sensor is not initialised it will fall through to the next
|
||||
}
|
||||
else{
|
||||
// Move to next sensor
|
||||
ultrasonicNextPing = USONIC3_MASK;
|
||||
}
|
||||
// fallthrough
|
||||
case USONIC3_MASK:
|
||||
if(ultrasonicInit&USONIC3_MASK){
|
||||
// Reinitialise the pin to setup the interrupt
|
||||
YEEP144_GPIO_Init(USONIC3_ECHO_PORT, USONIC3_ECHO_PIN, GPIO_MODE_IT_RISING_FALLING);
|
||||
|
||||
// Toggle the pin on for 10uS
|
||||
HAL_GPIO_WritePin(USONIC3_TRIG_PORT, USONIC3_TRIG_PIN, GPIO_PIN_SET);
|
||||
delayUS(USONIC_PING_LEN);
|
||||
HAL_GPIO_WritePin(USONIC3_TRIG_PORT, USONIC3_TRIG_PIN, GPIO_PIN_RESET);
|
||||
|
||||
// Set the ping bit so we know this sensor is pinging
|
||||
ultrasonicPing |= USONIC3_MASK;
|
||||
|
||||
// Move to next sensor
|
||||
ultrasonicNextPing = USONIC4_MASK;
|
||||
break; // Break is within the if statement, so if a sensor is not initialised it will fall through to the next
|
||||
}
|
||||
else{
|
||||
// Move to next sensor
|
||||
ultrasonicNextPing = USONIC4_MASK;
|
||||
}
|
||||
// fallthrough
|
||||
case USONIC4_MASK:
|
||||
if(ultrasonicInit&USONIC4_MASK){
|
||||
// As the interrupt line for ultrasonic 4 is shared with pushbutton 3,
|
||||
// we want to save the EXTI configuration to restore when pinging is finished
|
||||
HAL_EXTI_GetHandle(&hexti12, EXTI_LINE_12);
|
||||
HAL_EXTI_GetConfigLine(&hexti12, &hexti12_conf);
|
||||
|
||||
// Reinitialise the pin to setup the interrupt
|
||||
YEEP144_GPIO_Init(USONIC4_ECHO_PORT, USONIC4_ECHO_PIN, GPIO_MODE_IT_RISING_FALLING);
|
||||
|
||||
// Toggle the pin on for 10uS
|
||||
HAL_GPIO_WritePin(USONIC4_TRIG_PORT, USONIC4_TRIG_PIN, GPIO_PIN_SET);
|
||||
delayUS(USONIC_PING_LEN);
|
||||
HAL_GPIO_WritePin(USONIC4_TRIG_PORT, USONIC4_TRIG_PIN, GPIO_PIN_RESET);
|
||||
|
||||
// Set the ping bit so we know this sensor is pinging
|
||||
ultrasonicPing |= USONIC4_MASK;
|
||||
|
||||
// Move to next sensor
|
||||
ultrasonicNextPing = USONIC1_MASK;
|
||||
break; // Break is within the if statement, so if a sensor is not initialised it will fall through to the next
|
||||
}
|
||||
else{
|
||||
// Move to next sensor
|
||||
ultrasonicNextPing = USONIC1_MASK;
|
||||
}
|
||||
|
||||
}
|
||||
// If a ping was successfully started return the status byte, else return 255
|
||||
if(!ultrasonicPing){
|
||||
return ultrasonicPing;
|
||||
}
|
||||
else{
|
||||
// This should only happen if this function is called with no sensor initialised
|
||||
return 255;
|
||||
}
|
||||
}
|
||||
else{
|
||||
// Returns 0 if a sensor is already pinging
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a reading on ultrasonic 1 using the non-blocking method
|
||||
*
|
||||
* If no sensor is currently initialised returns 255
|
||||
* If a sensor is already pinging, return 0
|
||||
* If a new reading is successfully started, return the status byte indicating which sensor is currently pinging
|
||||
*
|
||||
* YEEP144_Ultrasonic1_Callback will be called when a new value is available
|
||||
*/
|
||||
uint8_t YEEP144_Ultrasonic1_Ping(void){
|
||||
ultrasonicNextPing = USONIC1_MASK;
|
||||
return YEEP144_Ultrasonic_PingNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a reading on ultrasonic 2 using the non-blocking method
|
||||
*
|
||||
* If no sensor is currently initialised returns 255
|
||||
* If a sensor is already pinging, return 0
|
||||
* If a new reading is successfully started, return the status byte indicating which sensor is currently pinging
|
||||
*
|
||||
* YEEP144_Ultrasonic2_Callback will be called when a new value is available
|
||||
*/
|
||||
uint8_t YEEP144_Ultrasonic2_Ping(void){
|
||||
ultrasonicNextPing = USONIC2_MASK;
|
||||
return YEEP144_Ultrasonic_PingNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a reading on ultrasonic 3 using the non-blocking method
|
||||
*
|
||||
* If no sensor is currently initialised returns 255
|
||||
* If a sensor is already pinging, return 0
|
||||
* If a new reading is successfully started, return the status byte indicating which sensor is currently pinging
|
||||
*
|
||||
* YEEP144_Ultrasonic3_Callback will be called when a new value is available
|
||||
*/
|
||||
uint8_t YEEP144_Ultrasonic3_Ping(void){
|
||||
ultrasonicNextPing = USONIC3_MASK;
|
||||
return YEEP144_Ultrasonic_PingNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a reading on ultrasonic 4 using the non-blocking method
|
||||
*
|
||||
* If no sensor is currently initialised returns 255
|
||||
* If a sensor is already pinging, return 0
|
||||
* If a new reading is successfully started, return the status byte indicating which sensor is currently pinging
|
||||
*
|
||||
* YEEP144_Ultrasonic4_Callback will be called when a new value is available
|
||||
*/
|
||||
uint8_t YEEP144_Ultrasonic4_Ping(void){
|
||||
ultrasonicNextPing = USONIC4_MASK;
|
||||
return YEEP144_Ultrasonic_PingNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a reading on ultrasonic 1 with blocking waits until the new value is available
|
||||
*
|
||||
* Returns 0 if the sensor is not initialised, or the function times out, otherwise returns the tick value
|
||||
*
|
||||
* YEEP144_Ultrasonic1_Callback will still be called when the new value is available
|
||||
*
|
||||
*/
|
||||
uint16_t YEEP144_Ultrasonic1_PingBlocking(void){
|
||||
|
||||
uint32_t blockingTimeoutStart = HAL_GetTick();
|
||||
|
||||
uint8_t pingReturn;
|
||||
// Attempt pings until successful
|
||||
// If 255 is returned, sensor is uninitialised, so fail and return 0
|
||||
// If we exceed timeout then return 0;
|
||||
do{
|
||||
pingReturn = YEEP144_Ultrasonic1_Ping();
|
||||
if(pingReturn == 255) return 0;
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
} while(pingReturn != USONIC1_MASK);
|
||||
|
||||
// Wait until pinging starts
|
||||
// If we exceed timeout then return 0
|
||||
while(ultrasonicPing != USONIC1_MASK){
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
}
|
||||
|
||||
// Wait until pinging ends
|
||||
// If we exceed timeout then return 0
|
||||
while(ultrasonicPing == USONIC1_MASK){
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
}
|
||||
|
||||
// Return the read value
|
||||
return ultrasonicTickLen[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a reading on ultrasonic 2 with blocking waits until the new value is available
|
||||
*
|
||||
* Returns 0 if the sensor is not initialised, or the function times out, otherwise returns the tick value
|
||||
*
|
||||
* YEEP144_Ultrasonic1_Callback will still be called when the new value is available
|
||||
*
|
||||
*/
|
||||
uint16_t YEEP144_Ultrasonic2_PingBlocking(void){
|
||||
|
||||
uint32_t blockingTimeoutStart = HAL_GetTick();
|
||||
|
||||
uint8_t pingReturn;
|
||||
// Attempt pings until successful
|
||||
// If 255 is returned, sensor is uninitialised, so fail and return 0
|
||||
// If we exceed timeout then return 0;
|
||||
do{
|
||||
pingReturn = YEEP144_Ultrasonic2_Ping();
|
||||
if(pingReturn == 255) return 0;
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
} while(pingReturn != USONIC2_MASK);
|
||||
|
||||
// Wait until pinging starts
|
||||
// If we exceed timeout then return 0
|
||||
while(ultrasonicPing != USONIC2_MASK){
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
}
|
||||
|
||||
// Wait until pinging ends
|
||||
// If we exceed timeout then return 0
|
||||
while(ultrasonicPing == USONIC2_MASK){
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
}
|
||||
|
||||
// Return the read value
|
||||
return ultrasonicTickLen[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a reading on ultrasonic 3 with blocking waits until the new value is available
|
||||
*
|
||||
* Returns 0 if the sensor is not initialised, or the function times out, otherwise returns the tick value
|
||||
*
|
||||
* YEEP144_Ultrasonic1_Callback will still be called when the new value is available
|
||||
*
|
||||
*/
|
||||
uint16_t YEEP144_Ultrasonic3_PingBlocking(void){
|
||||
|
||||
uint32_t blockingTimeoutStart = HAL_GetTick();
|
||||
|
||||
uint8_t pingReturn;
|
||||
// Attempt pings until successful
|
||||
// If 255 is returned, sensor is uninitialised, so fail and return 0
|
||||
// If we exceed timeout then return 0;
|
||||
do{
|
||||
pingReturn = YEEP144_Ultrasonic3_Ping();
|
||||
if(pingReturn == 255) return 0;
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
} while(pingReturn != USONIC3_MASK);
|
||||
|
||||
// Wait until pinging starts
|
||||
// If we exceed timeout then return 0
|
||||
while(ultrasonicPing != USONIC3_MASK){
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
}
|
||||
|
||||
// Wait until pinging ends
|
||||
// If we exceed timeout then return 0
|
||||
while(ultrasonicPing == USONIC3_MASK){
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
}
|
||||
|
||||
// Return the read value
|
||||
return ultrasonicTickLen[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a reading on ultrasonic 4 with blocking waits until the new value is available
|
||||
*
|
||||
* Returns 0 if the sensor is not initialised, or the function times out, otherwise returns the tick value
|
||||
*
|
||||
* YEEP144_Ultrasonic1_Callback will still be called when the new value is available
|
||||
*
|
||||
*/
|
||||
uint16_t YEEP144_Ultrasonic4_PingBlocking(void){
|
||||
|
||||
uint32_t blockingTimeoutStart = HAL_GetTick();
|
||||
|
||||
uint8_t pingReturn;
|
||||
// Attempt pings until successful
|
||||
// If 255 is returned, sensor is uninitialised, so fail and return 0
|
||||
// If we exceed timeout then return 0;
|
||||
do{
|
||||
pingReturn = YEEP144_Ultrasonic4_Ping();
|
||||
if(pingReturn == 255) return 0;
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
} while(pingReturn != USONIC4_MASK);
|
||||
|
||||
// Wait until pinging starts
|
||||
// If we exceed timeout then return 0
|
||||
while(ultrasonicPing != USONIC4_MASK){
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
}
|
||||
|
||||
// Wait until pinging ends
|
||||
// If we exceed timeout then return 0
|
||||
while(ultrasonicPing == USONIC4_MASK){
|
||||
if((HAL_GetTick()- blockingTimeoutStart) >= BLOCKING_TIMEOUT_TICKS) return 0;
|
||||
}
|
||||
|
||||
// Return the read value
|
||||
return ultrasonicTickLen[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the interrupt for sensors 1, 2, and 3 connected to EXTI 10
|
||||
* This should be called from the main EXTI10 IRQ handler found in YEEP144_IRQ.c
|
||||
*/
|
||||
void YEEP144_Ultrasonic_EXTI10Handler(void){
|
||||
// Store the value from the timer at the start of the ISR to avoid execution time affecting the result
|
||||
uint16_t TIMVal = __HAL_TIM_GET_COUNTER(&htim6);
|
||||
|
||||
// Rising edge interrupt means pulse is starting
|
||||
if(__HAL_GPIO_EXTI_GET_RISING_IT(USONIC1_ECHO_PIN)){
|
||||
// Check to see which sensor is currently pinging based on the GPIO and the ping status byte
|
||||
if((HAL_GPIO_ReadPin(USONIC1_ECHO_PORT, USONIC1_ECHO_PIN) == GPIO_PIN_SET) && (ultrasonicPing & USONIC1_MASK)){
|
||||
// Store the starting timer value and set the relevant status bit
|
||||
ultrasonicTickStart[0] = TIMVal;
|
||||
ultrasonicEcho |= USONIC1_MASK;
|
||||
}
|
||||
if((HAL_GPIO_ReadPin(USONIC2_ECHO_PORT, USONIC2_ECHO_PIN) == GPIO_PIN_SET) && (ultrasonicPing & USONIC2_MASK)){
|
||||
ultrasonicTickStart[1] = TIMVal;
|
||||
ultrasonicEcho |= USONIC2_MASK;
|
||||
}
|
||||
if((HAL_GPIO_ReadPin(USONIC3_ECHO_PORT, USONIC3_ECHO_PIN) == GPIO_PIN_SET) && (ultrasonicPing & USONIC3_MASK)){
|
||||
ultrasonicTickStart[2] = TIMVal;
|
||||
ultrasonicEcho |= USONIC3_MASK;
|
||||
}
|
||||
__HAL_GPIO_EXTI_CLEAR_RISING_IT(USONIC1_ECHO_PIN); // Clear the interrupt
|
||||
return;
|
||||
}
|
||||
// Falling edge interrupt means pulse is ending
|
||||
else if(__HAL_GPIO_EXTI_GET_FALLING_IT(USONIC1_ECHO_PIN)){
|
||||
// Check which sensor is pinging based on the GPIO and status byte
|
||||
if((HAL_GPIO_ReadPin(USONIC1_ECHO_PORT, USONIC1_ECHO_PIN) == GPIO_PIN_RESET) && (ultrasonicEcho & USONIC1_MASK)){
|
||||
// Reset the status bits
|
||||
ultrasonicPing &= ~USONIC1_MASK;
|
||||
ultrasonicEcho &= ~USONIC1_MASK;
|
||||
ultrasonicTickLen[0] = TIMVal - ultrasonicTickStart[0]; // Calculate the time elapsed
|
||||
YEEP144_Ultrasonic1_Callback(ultrasonicTickLen[0]); // Call the relevant callback function to signal the reading is done
|
||||
}
|
||||
if((HAL_GPIO_ReadPin(USONIC2_ECHO_PORT, USONIC2_ECHO_PIN) == GPIO_PIN_RESET) && (ultrasonicEcho & USONIC2_MASK)){
|
||||
ultrasonicPing &= ~USONIC2_MASK;
|
||||
ultrasonicEcho &= ~USONIC2_MASK;
|
||||
ultrasonicTickLen[1] = TIMVal - ultrasonicTickStart[1];
|
||||
YEEP144_Ultrasonic2_Callback(ultrasonicTickLen[1]);
|
||||
}
|
||||
if((HAL_GPIO_ReadPin(USONIC3_ECHO_PORT, USONIC3_ECHO_PIN) == GPIO_PIN_RESET) && (ultrasonicEcho & USONIC3_MASK)){
|
||||
ultrasonicPing &= ~USONIC3_MASK;
|
||||
ultrasonicEcho &= ~USONIC3_MASK;
|
||||
ultrasonicTickLen[2] = TIMVal - ultrasonicTickStart[2];
|
||||
YEEP144_Ultrasonic3_Callback(ultrasonicTickLen[2]);
|
||||
}
|
||||
__HAL_GPIO_EXTI_CLEAR_FALLING_IT(USONIC1_ECHO_PIN); // Clear the interrupt
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the interrupt for sensor 4 connected to EXTI 12
|
||||
* This should be called from the main EXTI12 IRQ handler found in YEEP144_IRQ.c
|
||||
*/
|
||||
void YEEP144_Ultrasonic_EXTI12Handler(void){
|
||||
// Store the value from the timer at the start of the ISR to avoid execution time affecting the result
|
||||
uint16_t TIMVal = __HAL_TIM_GET_COUNTER(&htim6);
|
||||
|
||||
// Rising edge interrupt means pulse is starting
|
||||
if(__HAL_GPIO_EXTI_GET_RISING_IT(USONIC4_ECHO_PIN)){
|
||||
// Check to see which sensor is currently pinging based on the GPIO and the ping status byte
|
||||
if((HAL_GPIO_ReadPin(USONIC4_ECHO_PORT, USONIC4_ECHO_PIN) == GPIO_PIN_SET) && (ultrasonicPing & USONIC4_MASK)){
|
||||
// Store the starting timer value and set the relevant status bit
|
||||
ultrasonicTickStart[3] = TIMVal;
|
||||
ultrasonicEcho |= USONIC4_MASK;
|
||||
}
|
||||
__HAL_GPIO_EXTI_CLEAR_RISING_IT(USONIC4_ECHO_PIN); // Clear the interrupt
|
||||
return;
|
||||
}
|
||||
// Falling edge interrupt means pulse is ending
|
||||
else if(__HAL_GPIO_EXTI_GET_FALLING_IT(USONIC4_ECHO_PIN)){
|
||||
// Check which sensor is pinging based on the GPIO and status byte
|
||||
if((HAL_GPIO_ReadPin(USONIC4_ECHO_PORT, USONIC4_ECHO_PIN) == GPIO_PIN_RESET) && (ultrasonicEcho & USONIC4_MASK)){
|
||||
// Reset the status bits
|
||||
ultrasonicPing &= ~USONIC4_MASK;
|
||||
ultrasonicEcho &= ~USONIC4_MASK;
|
||||
ultrasonicTickLen[3] = TIMVal - ultrasonicTickStart[3]; // Calculate the time elapsed
|
||||
YEEP144_Ultrasonic4_Callback(ultrasonicTickLen[3]); // Call the relevant callback function to signal the reading is done
|
||||
}
|
||||
__HAL_GPIO_EXTI_CLEAR_FALLING_IT(USONIC4_ECHO_PIN); // Clear the interrupt
|
||||
// Restore the previous exti configuration
|
||||
HAL_EXTI_GetHandle(&hexti12, EXTI_LINE_12);
|
||||
HAL_EXTI_SetConfigLine(&hexti12, &hexti12_conf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert timer ticks to a centimeter value
|
||||
*/
|
||||
float YEEP144_Ultrasonic_TickTocm(uint16_t ticks){
|
||||
// Total number of ticks is return trip time, we halve it to get distance to object
|
||||
// Speed of sound taken as 343m/s = 34300 cm/s = 0.0343 cm/us
|
||||
return ticks/2.0 * 0.0343;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used to initialise the timer used for measuring the received pulses
|
||||
*/
|
||||
void YEEP144_Ultrasonic_TIMInit(void){
|
||||
|
||||
// Enable the clock to the timer
|
||||
__HAL_RCC_TIM6_CLK_ENABLE();
|
||||
|
||||
// Create empty config structure
|
||||
TIM_MasterConfigTypeDef sMasterConfig = {0};
|
||||
|
||||
// Populate config structure. The should configure it for 1uS per tick, assuming 250MHz peripheral clock
|
||||
htim6.Instance = TIM6;
|
||||
htim6.Init.Prescaler = 249;
|
||||
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim6.Init.Period = 65535;
|
||||
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
if (HAL_TIM_Base_Init(&htim6) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Start the counting. This counter is left free running as relevant functions
|
||||
// calculate time elapsed by recording the value at the start and end of an event
|
||||
HAL_TIM_Base_Start(&htim6);
|
||||
|
||||
// Set the init flag so the timer doesn't get initialised multiple times
|
||||
ultrasonicTIMInit = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* These callback functions are weakly defined so they can be overwritten by the user's
|
||||
* own callback function. They have to exist at least in weak form so that the code compiles
|
||||
* even if the user chooses not to define their own
|
||||
*/
|
||||
__weak void YEEP144_Ultrasonic1_Callback(uint16_t ticks){
|
||||
|
||||
}
|
||||
|
||||
__weak void YEEP144_Ultrasonic2_Callback(uint16_t ticks){
|
||||
|
||||
}
|
||||
|
||||
__weak void YEEP144_Ultrasonic3_Callback(uint16_t ticks){
|
||||
|
||||
}
|
||||
|
||||
__weak void YEEP144_Ultrasonic4_Callback(uint16_t ticks){
|
||||
|
||||
}
|
||||
70
YEEP144lib/src/YEEP144_I2C.c
Normal file
70
YEEP144lib/src/YEEP144_I2C.c
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_I2C.h"
|
||||
#include "YEEP144_Pinout.h"
|
||||
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_gpio_ex.h"
|
||||
#include "stm32h5xx_hal_i2c.h"
|
||||
#include <stdint.h>
|
||||
|
||||
I2C_HandleTypeDef hi2c2;
|
||||
|
||||
void YEEP144_I2C_MuxSelect(uint8_t busNumber){
|
||||
uint8_t I2CBusSel;
|
||||
|
||||
// If busNumber is greater than 3, deselect all busses
|
||||
if(busNumber > 3){
|
||||
I2CBusSel = 0;
|
||||
}
|
||||
else{ // Else convert to 1-hot to activate bus
|
||||
I2CBusSel = 1 << busNumber;
|
||||
}
|
||||
|
||||
// Send byte to mux chip to select bus
|
||||
HAL_I2C_Master_Transmit(&hi2c2, I2C_MUX_ADDR, &I2CBusSel, 1, 1000);
|
||||
}
|
||||
|
||||
void YEEP144_I2C_Init(void){
|
||||
// Initialise and start the clock
|
||||
|
||||
// Structure to hold clock config
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
||||
|
||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2C2; // Configuring I2C2
|
||||
PeriphClkInitStruct.Lptim3ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; // Select PCLK1
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Enable the clock
|
||||
__HAL_RCC_I2C2_CLK_ENABLE();
|
||||
|
||||
// Configure the peripheral
|
||||
hi2c2.Instance = I2C2;
|
||||
hi2c2.Init.Timing = 0x60808CD3; // Magic number taken from MX for 100KHz - see Reference Manual RM0481, section 48.4.5
|
||||
|
||||
// We are not operating as a target, so the rest are just defaults
|
||||
hi2c2.Init.OwnAddress1 = 0;
|
||||
hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
|
||||
hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
|
||||
hi2c2.Init.OwnAddress2 = 0;
|
||||
hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
|
||||
hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
|
||||
hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
|
||||
if (HAL_I2C_Init(&hi2c2) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
//Configure Analogue filter (Enabled, which is default)
|
||||
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
//Configure Digital filter (Disabled, which is default)
|
||||
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Initialise GPIO pins
|
||||
YEEP144_GPIO_InitAdv(I2C_SDA_PORT, I2C_SDA_PIN | I2C_SCL_PIN, GPIO_MODE_AF_OD, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF4_I2C2);
|
||||
}
|
||||
241
YEEP144lib/src/YEEP144_IRQ.c
Normal file
241
YEEP144lib/src/YEEP144_IRQ.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* This file contains all of the IRQ handlers that are required for various parts
|
||||
* of the HAL and YEEP libraries to function. Unhandled IRQs will cause the system
|
||||
* to jump to the default handler, trapping it in an infinite loop
|
||||
*/
|
||||
|
||||
#include "YEEP144_HCSR04.h"
|
||||
#include "YEEP144_PushButton.h"
|
||||
#include "YEEP144_PiSPI.h"
|
||||
#include "stm32h563xx.h"
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_dma.h"
|
||||
#include "stm32h5xx_hal_spi.h"
|
||||
#include "stm32h5xx_hal_uart.h"
|
||||
|
||||
extern DMA_HandleTypeDef handle_GPDMA1_Channel0;
|
||||
extern DMA_HandleTypeDef handle_GPDMA1_Channel1;
|
||||
extern UART_HandleTypeDef huart2;
|
||||
extern UART_HandleTypeDef huart3;
|
||||
extern SPI_HandleTypeDef hspi4;
|
||||
|
||||
void YEEP144_BluetoothUART_MatchCallback(void);
|
||||
void YEEP144_DebugUART_MatchCallback(void);
|
||||
|
||||
/**
|
||||
* Handler for the SysTick interrupt
|
||||
* The HAL configures the SysTick to generate an interrupt every 1ms
|
||||
* HAL_IncTick needs to be called in here for the HAL to function properly
|
||||
*/
|
||||
void SysTick_Handler(void){
|
||||
HAL_IncTick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for EXTI line 6. In the YEEP libraries this is used by the yellow pushbutton
|
||||
*/
|
||||
void EXTI6_IRQHandler(void){
|
||||
YEEP144_PushButton_YellowHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for EXTI line 7. In the YEEP libraries this is used by the green pushbutton
|
||||
*/
|
||||
void EXTI7_IRQHandler(void){
|
||||
YEEP144_PushButton_GreenHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for EXTI line 10. In the YEEP libraries this is used by ultrasonic sensors 1, 2, and 3
|
||||
*/
|
||||
void EXTI10_IRQHandler(void){
|
||||
YEEP144_Ultrasonic_EXTI10Handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for EXTI line 12. In the YEEP libraries this is used by ultrasonic sensor 4, and pushbutton 3 / red
|
||||
*/
|
||||
void EXTI12_IRQHandler(void){
|
||||
YEEP144_PushButton_RedHandler();
|
||||
YEEP144_Ultrasonic_EXTI12Handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for EXTI line 14. In the YEEP libraries this is used by the blue pushbutton
|
||||
*/
|
||||
void EXTI13_IRQHandler(void){
|
||||
YEEP144_PushButton_BlueHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for GPDMA1 channel 0. In the YEEP libraries this is used by the BT UART reception
|
||||
* We just call the relevant HAL handler to let it deal with it
|
||||
*/
|
||||
void GPDMA1_CH0_IRQHandler(void){
|
||||
HAL_DMA_IRQHandler(&handle_GPDMA1_Channel0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for GPDMA1 channel 0. In the YEEP libraries this is used by the BT UART reception
|
||||
* We just call the relevant HAL handler to let it deal with it
|
||||
*/
|
||||
void GPDMA1_CH1_IRQHandler(void){
|
||||
HAL_DMA_IRQHandler(&handle_GPDMA1_Channel1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for SPI4 IRQ. We just call the relevant HAL handler to let it deal with it
|
||||
*/
|
||||
void SPI4_IRQHandler(void){
|
||||
HAL_SPI_IRQHandler(&hspi4);
|
||||
}
|
||||
|
||||
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi){
|
||||
if(hspi->Instance == SPI4){
|
||||
YEEP144_PiSPI_TxRxHandler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for USART2. In the YEEP libraries this is used by the BT UART.
|
||||
* We do our own checks for the character match interrupt as this is not handled
|
||||
* by the HAL. For any other interrupt we just let the HAL handle it
|
||||
*/
|
||||
void USART2_IRQHandler(void){
|
||||
uint32_t isrflags = READ_REG(huart2.Instance->ISR);
|
||||
uint32_t cr1its = READ_REG(huart2.Instance->CR1);
|
||||
|
||||
// If the character match is enabled and character match flag raised
|
||||
if (((isrflags & USART_ISR_CMF) != 0U) && ((cr1its & USART_CR1_CMIE) != 0U)){
|
||||
__HAL_UART_CLEAR_FLAG(&huart2, UART_CLEAR_CMF); // Clear the character match flag
|
||||
HAL_DMA_Abort(huart2.hdmarx); // End the current DMA
|
||||
// Reset the state of the UART HAL
|
||||
huart2.RxState = HAL_UART_STATE_READY;
|
||||
huart2.ReceptionType = HAL_UART_RECEPTION_STANDARD;
|
||||
|
||||
// Clear the idle line bit so the reception can be rearmed
|
||||
ATOMIC_CLEAR_BIT(huart2.Instance->CR1, USART_CR1_IDLEIE);
|
||||
|
||||
// Call the match callback
|
||||
YEEP144_BluetoothUART_MatchCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is not the character match IRQ, let the HAL handle it
|
||||
HAL_UART_IRQHandler(&huart2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for USART3. In the YEEP libraries this is used by the BT UART.
|
||||
* We do our own checks for the character match interrupt as this is not handled
|
||||
* by the HAL. For any other interrupt we just let the HAL handle it
|
||||
*/
|
||||
void USART3_IRQHandler(void){
|
||||
uint32_t isrflags = READ_REG(huart3.Instance->ISR);
|
||||
uint32_t cr1its = READ_REG(huart3.Instance->CR1);
|
||||
|
||||
// If the character match is enabled and character match flag raised
|
||||
if (((isrflags & USART_ISR_CMF) != 0U) && ((cr1its & USART_CR1_CMIE) != 0U)){
|
||||
__HAL_UART_CLEAR_FLAG(&huart3, UART_CLEAR_CMF); // Clear the character match flag
|
||||
HAL_DMA_Abort(huart3.hdmarx); // End the current DMA
|
||||
// Reset the state of the UART HAL
|
||||
huart3.RxState = HAL_UART_STATE_READY;
|
||||
huart3.ReceptionType = HAL_UART_RECEPTION_STANDARD;
|
||||
|
||||
// Clear the idle line bit so the reception can be rearmed
|
||||
ATOMIC_CLEAR_BIT(huart3.Instance->CR1, USART_CR1_IDLEIE);
|
||||
|
||||
// Call the match callback
|
||||
YEEP144_DebugUART_MatchCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is not the character match IRQ, let the HAL handle it
|
||||
HAL_UART_IRQHandler(&huart3);
|
||||
}
|
||||
|
||||
/**
|
||||
* These weakly defined callback functions are here so that the code always compiles
|
||||
* You shouldn't edit these functions, but instead implement your own with the exact same name
|
||||
* in one of your own files, like main.c
|
||||
*/
|
||||
__weak void YEEP144_BluetoothUART_MatchCallback(void){
|
||||
return;
|
||||
}
|
||||
|
||||
__weak void YEEP144_DebugUART_MatchCallback(void){
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* These functions copied from STM's interrupt code to provide stubs for handling
|
||||
* the internal Cortex Processor interrupts and exceptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* This function handles Non maskable interrupt.
|
||||
*/
|
||||
void NMI_Handler(void){
|
||||
while (1){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles Hard fault interrupt.
|
||||
*/
|
||||
void HardFault_Handler(void){
|
||||
while (1){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles Memory management fault.
|
||||
*/
|
||||
void MemManage_Handler(void){
|
||||
while (1){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles Prefetch fault, memory access fault.
|
||||
*/
|
||||
void BusFault_Handler(void){
|
||||
while (1){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles Undefined instruction or illegal state.
|
||||
*/
|
||||
void UsageFault_Handler(void){
|
||||
while (1){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles System service call via SWI instruction.
|
||||
*/
|
||||
void SVC_Handler(void){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles Debug monitor.
|
||||
*/
|
||||
void DebugMon_Handler(void){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles Pendable request for system service.
|
||||
*/
|
||||
void PendSV_Handler(void){
|
||||
|
||||
}
|
||||
173
YEEP144lib/src/YEEP144_PiSPI.c
Normal file
173
YEEP144lib/src/YEEP144_PiSPI.c
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "YEEP144_Pinout.h"
|
||||
#include "stm32h563xx.h"
|
||||
#include "stm32h5xx.h"
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_cortex.h"
|
||||
#include "stm32h5xx_hal_def.h"
|
||||
#include "stm32h5xx_hal_gpio.h"
|
||||
#include "stm32h5xx_hal_gpio_ex.h"
|
||||
#include "stm32h5xx_hal_rcc.h"
|
||||
#include "stm32h5xx_hal_spi.h"
|
||||
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_PiSPI.h"
|
||||
#include "stm32h5xx_hal_spi_ex.h"
|
||||
#include <string.h>
|
||||
|
||||
void YEEP144_PiSPI_RxCallback(void);
|
||||
|
||||
SPI_HandleTypeDef hspi4;
|
||||
|
||||
uint8_t PiSPIRxBuf[PISPI_BUFFER_SIZE];
|
||||
|
||||
uint8_t PiSPITxBuf[PISPI_BUFFER_SIZE];
|
||||
uint8_t PiSPITxBufInternal[PISPI_BUFFER_SIZE];
|
||||
|
||||
/**
|
||||
* Initialises SPI communications for use with an attached Raspberry Pi. In this configuration the Pi will act as the master
|
||||
* therefore the STM32 will be configured as a slave. This means that the master can send / ask for data at any time, and the SPI
|
||||
* peripheral must be ready to avoid under/overruns.
|
||||
*/
|
||||
void YEEP144_PiSPI_Init(void){
|
||||
|
||||
// Setup and enable clock
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI4;
|
||||
PeriphClkInitStruct.Spi4ClockSelection = RCC_SPI4CLKSOURCE_HSI; // HSI = 64MHz clock
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
__HAL_RCC_SPI4_CLK_ENABLE();
|
||||
|
||||
// Setup GPIO
|
||||
YEEP144_GPIO_InitAdv(SPI_MOSI_PORT, SPI_MOSI_PIN | SPI_MISO_PIN | SPI_SCK_PIN , GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF5_SPI4);
|
||||
YEEP144_GPIO_Init(SPI_CS_PORT, SPI_CS_PIN, GPIO_MODE_OUTPUT);
|
||||
YEEP144_PiSPI_ResetReady();
|
||||
|
||||
// Enable interrupt
|
||||
HAL_NVIC_SetPriority(SPI4_IRQn, 0, 0);
|
||||
HAL_NVIC_EnableIRQ(SPI4_IRQn);
|
||||
|
||||
hspi4.Instance = SPI4;
|
||||
hspi4.Init.Mode = SPI_MODE_SLAVE; // Slave mode
|
||||
hspi4.Init.Direction = SPI_DIRECTION_2LINES; // Full duplex transmission
|
||||
hspi4.Init.DataSize = SPI_DATASIZE_8BIT; // Standard 8-bit word size
|
||||
hspi4.Init.CLKPolarity = SPI_POLARITY_LOW; // Clock polarity and phase set for SPI mode 0
|
||||
hspi4.Init.CLKPhase = SPI_PHASE_1EDGE;
|
||||
hspi4.Init.NSS = SPI_NSS_SOFT; // Chip Select managed by software - we assume we are always selected so the CS line can be repurposed for the ready signal
|
||||
hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB; // MSB first
|
||||
hspi4.Init.TIMode = SPI_TIMODE_DISABLE; // Not using TI mode
|
||||
hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // Not using CRC
|
||||
hspi4.Init.CRCPolynomial = 0x7;
|
||||
hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; // Chip select is held not pulsed
|
||||
hspi4.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; // Chip select is active low
|
||||
hspi4.Init.FifoThreshold = SPI_FIFO_THRESHOLD_04DATA; // FIFO threshold is 4-bytes which is the maximum
|
||||
hspi4.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; // Master configuration is defaults as we aren't master
|
||||
hspi4.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
|
||||
hspi4.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
|
||||
hspi4.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
|
||||
hspi4.Init.IOSwap = SPI_IO_SWAP_DISABLE; // Don't need to swap IO pins
|
||||
hspi4.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY; // We aren't using the hardware ready signal
|
||||
hspi4.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;
|
||||
if (HAL_SPI_Init(&hspi4) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Set up our interrupt to wait for start of transfer from the master
|
||||
HAL_SPI_TransmitReceive_IT(&hspi4, PiSPITxBufInternal, PiSPIRxBuf, PISPI_BUFFER_SIZE);
|
||||
|
||||
// Set the ready flag as we are now ready to communicate
|
||||
YEEP144_PiSPI_SetReady();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the ready signal to ask the master to stop sending / requesting data
|
||||
*/
|
||||
void YEEP144_PiSPI_ResetReady(void){
|
||||
HAL_GPIO_WritePin(SPI_CS_PORT, SPI_CS_PIN, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ready signal to tell the master we are ready to receive data
|
||||
*/
|
||||
void YEEP144_PiSPI_SetReady(void){
|
||||
HAL_GPIO_WritePin(SPI_CS_PORT, SPI_CS_PIN, GPIO_PIN_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the latest data into the SPI peripheral. Call this function every time you change PiSPITxBuf.
|
||||
*/
|
||||
void YEEP144_PiSPI_LoadTx(void){
|
||||
/**
|
||||
* As we cannot know when the master will initiate a transfer, we must always be ready to receive and send data
|
||||
* to avoid overrun when receiving, or underrun when sending. This means that the SPI peripheral must be active
|
||||
* with a transfer pending before the master begins a transfer by begining the clock and sending data, and
|
||||
* this pending transfer must be kept active as much of the time as possible. This means that we always start a
|
||||
* transfer with HAL_SPI_TransmitReceive_IT at the end of SPI initialisation, and immediately after the previous
|
||||
* transfer has finished in the relevant ISR.
|
||||
*
|
||||
* When a transfer is started with HAL_SPI_TransmitReceive_IT, the first x bytes from the transmit buffer is
|
||||
* pre-loaded into the SPI peripheral's internal TX FIFO, with x depending on the FIFO Threshold configuration,
|
||||
* to avoid any issues with data underrun. This means that if data in the transmit buffer is changed after a
|
||||
* transfer is started, the first x bytes will remain from the state of the buffer when the transfer was started,
|
||||
* leading to messages with a mix of old and new data.
|
||||
*
|
||||
* In order to avoid this, the transmit data is double buffered, so that the user code cannot change the data while
|
||||
* a transfer is in progress, and this function aborts the transfer, which flushes the FIFO, and copies the front
|
||||
* buffer into the back buffer, before restarting the transfer, which prevents the buffer changeover from also
|
||||
* occurring mid transaction.
|
||||
*
|
||||
* This means there is a small window each time this function is called, and shortly after the end of a transfer
|
||||
* where the SPI will not be active as the data is changed over. The ready signal should be reset during this period
|
||||
* which will lead to a small delay on the master side
|
||||
*/
|
||||
|
||||
// Wait for current transfer to end
|
||||
while(((SPI4->SR & SPI_SR_CTSIZE_Msk) >> SPI_SR_CTSIZE_Pos) != PISPI_BUFFER_SIZE){}
|
||||
|
||||
// Clear the ready signal to tell the master we are no longer ready
|
||||
YEEP144_PiSPI_ResetReady();
|
||||
|
||||
// Stop the current transfer which flushes the old data from the FIFO
|
||||
HAL_SPI_Abort_IT(&hspi4);
|
||||
|
||||
// Copy the user facing buffer into the internal buffer
|
||||
memcpy(PiSPITxBufInternal, PiSPITxBuf, PISPI_BUFFER_SIZE);
|
||||
|
||||
// Restart the transfer now the buffer has been updated
|
||||
HAL_SPI_TransmitReceive_IT(&hspi4, PiSPITxBufInternal, PiSPIRxBuf, PISPI_BUFFER_SIZE);
|
||||
|
||||
// Set the ready signal to tell the master we are now ready to continue communication
|
||||
YEEP144_PiSPI_SetReady();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should be called by HAL_SPI_TxRxCpltCallback in YEEP144_IRQ to handle the end of a transfer
|
||||
* It loads the latest data into the transmit buffer, reinitialises the SPI peripheral ready for the next transfer,
|
||||
* and calls the user callback
|
||||
*/
|
||||
void YEEP144_PiSPI_TxRxHandler(void){
|
||||
|
||||
// Clear the ready signal as we are not ready to receive until the message has been handled
|
||||
YEEP144_PiSPI_ResetReady();
|
||||
|
||||
// Copy the user facing buffer into the internal buffer
|
||||
memcpy(PiSPITxBufInternal, PiSPITxBuf, PISPI_BUFFER_SIZE);
|
||||
|
||||
// Restart the transfer now the buffer has been updated
|
||||
HAL_SPI_TransmitReceive_IT(&hspi4, PiSPITxBufInternal, PiSPIRxBuf, PISPI_BUFFER_SIZE);
|
||||
|
||||
// Call the user callback function - the user will need to restore the ready signal when done
|
||||
// for more messages to be received
|
||||
YEEP144_PiSPI_RxCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a weakly defined function for the RX Callback function.
|
||||
* It should be overriden by an identically named function in user code
|
||||
*/
|
||||
__weak void YEEP144_PiSPI_RxCallback(void){
|
||||
return;
|
||||
}
|
||||
111
YEEP144lib/src/YEEP144_PushButton.c
Normal file
111
YEEP144lib/src/YEEP144_PushButton.c
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "YEEP144_PushButton.h"
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_Pinout.h"
|
||||
#include "stm32h5xx_hal_cortex.h"
|
||||
#include "stm32h5xx_hal_def.h"
|
||||
#include "stm32h5xx_hal_gpio.h"
|
||||
|
||||
void YEEP144_PushButton_BlueCallback(void);
|
||||
void YEEP144_PushButton_GreenCallback(void);
|
||||
void YEEP144_PushButton_YellowCallback(void);
|
||||
void YEEP144_PushButton_RedCallback(void);
|
||||
|
||||
/**
|
||||
* Initialise the blue button. This consumes the button's GPIO and EXTI13
|
||||
*/
|
||||
void YEEP144_PushButton_BlueInit(void){
|
||||
YEEP144_GPIO_Init(BUTTON_BLUE_PORT, BUTTON_BLUE_PIN, GPIO_MODE_IT_FALLING);
|
||||
HAL_NVIC_SetPriority(BUTTON_BLUE_EXTI, 1, 0);
|
||||
HAL_NVIC_EnableIRQ(BUTTON_BLUE_EXTI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the green button. This consumes the button's GPIO and EXTI7
|
||||
*/
|
||||
void YEEP144_PushButton_GreenInit(void){
|
||||
YEEP144_GPIO_Init(BUTTON_GREEN_PORT, BUTTON_GREEN_PIN, GPIO_MODE_IT_FALLING);
|
||||
HAL_NVIC_SetPriority(BUTTON_GREEN_EXTI, 1, 0);
|
||||
HAL_NVIC_EnableIRQ(BUTTON_GREEN_EXTI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the yellow button. This consumes the button's GPIO and EXTI6
|
||||
*/
|
||||
void YEEP144_PushButton_YellowInit(void){
|
||||
YEEP144_GPIO_Init(BUTTON_YELLOW_PORT, BUTTON_YELLOW_PIN, GPIO_MODE_IT_FALLING);
|
||||
HAL_NVIC_SetPriority(BUTTON_YELLOW_EXTI, 1, 0);
|
||||
HAL_NVIC_EnableIRQ(BUTTON_YELLOW_EXTI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the blue button. This consumes the button's GPIO and EXTI12 (shared with ultrasonic 4)
|
||||
*/
|
||||
void YEEP144_PushButton_RedInit(void){
|
||||
YEEP144_GPIO_Init(BUTTON_RED_PORT, BUTTON_RED_PIN, GPIO_MODE_IT_FALLING);
|
||||
HAL_NVIC_SetPriority(BUTTON_RED_EXTI, 1, 0);
|
||||
HAL_NVIC_EnableIRQ(BUTTON_RED_EXTI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the interrupt for the blue button. This should be called from the main EXTI13 handler, and in turn calls YEEP144_PushButton_BlueCallback
|
||||
*/
|
||||
void YEEP144_PushButton_BlueHandler(void){
|
||||
// Check this interrupt has been called due to the pushbutton
|
||||
if(__HAL_GPIO_EXTI_GET_IT(BUTTON_BLUE_PIN) && (HAL_GPIO_ReadPin(BUTTON_BLUE_PORT, BUTTON_BLUE_PIN) == GPIO_PIN_RESET)){
|
||||
__HAL_GPIO_EXTI_CLEAR_IT(BUTTON_BLUE_PIN); // Clear the interrupt
|
||||
YEEP144_PushButton_BlueCallback(); // Call the callback function
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the interrupt for the green button. This should be called from the main EXTI7 handler, and in turn calls YEEP144_PushButton_GreenCallback
|
||||
*/
|
||||
void YEEP144_PushButton_GreenHandler(void){
|
||||
// Check this interrupt has been called due to the pushbutton
|
||||
if(__HAL_GPIO_EXTI_GET_IT(BUTTON_GREEN_PIN) && (HAL_GPIO_ReadPin(BUTTON_GREEN_PORT, BUTTON_GREEN_PIN) == GPIO_PIN_RESET)){
|
||||
__HAL_GPIO_EXTI_CLEAR_IT(BUTTON_GREEN_PIN); // Clear the interrupt
|
||||
YEEP144_PushButton_GreenCallback(); // Call the callback function
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the interrupt for the yellow button. This should be called from the main EXTI6 handler, and in turn calls YEEP144_PushButton_YellowCallback
|
||||
*/
|
||||
void YEEP144_PushButton_YellowHandler(void){
|
||||
// Check this interrupt has been called due to the pushbutton
|
||||
if(__HAL_GPIO_EXTI_GET_IT(BUTTON_YELLOW_PIN) && (HAL_GPIO_ReadPin(BUTTON_YELLOW_PORT, BUTTON_YELLOW_PIN) == GPIO_PIN_RESET)){
|
||||
__HAL_GPIO_EXTI_CLEAR_IT(BUTTON_YELLOW_PIN); // Clear the interrupt
|
||||
YEEP144_PushButton_YellowCallback(); // Call the callback function
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the interrupt for the red button. This should be called from the main EXTI12 handler, and in turn calls YEEP144_PushButton_RedCallback
|
||||
*/
|
||||
void YEEP144_PushButton_RedHandler(void){
|
||||
// Check this interrupt has been called due to the pushbutton
|
||||
if(__HAL_GPIO_EXTI_GET_IT(BUTTON_RED_PIN) && (HAL_GPIO_ReadPin(BUTTON_RED_PORT, BUTTON_RED_PIN) == GPIO_PIN_RESET)){
|
||||
__HAL_GPIO_EXTI_CLEAR_IT(BUTTON_RED_PIN); // Clear the interrupt
|
||||
YEEP144_PushButton_RedCallback(); // Call the callback function
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* These versions of the callback functions are weakly defined so they can be overwritten by the user's function elsewhere.
|
||||
* They need to exist so that the code will still compile even if the user hasn't defined their own.
|
||||
*/
|
||||
__weak void YEEP144_PushButton_BlueCallback(void){
|
||||
|
||||
}
|
||||
|
||||
__weak void YEEP144_PushButton_GreenCallback(void){
|
||||
|
||||
}
|
||||
|
||||
__weak void YEEP144_PushButton_YellowCallback(void){
|
||||
|
||||
}
|
||||
|
||||
__weak void YEEP144_PushButton_RedCallback(void){
|
||||
|
||||
}
|
||||
148
YEEP144lib/src/YEEP144_RGB.c
Normal file
148
YEEP144lib/src/YEEP144_RGB.c
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_RGB.h"
|
||||
#include "YEEP144_I2C.h"
|
||||
#include "stm32h5xx_hal_i2c.h"
|
||||
|
||||
#define RGB_ADDR 0x50
|
||||
#define RGB_CHIP_EN_REG 0x000
|
||||
#define RGB_DEV_CONFIG_0_REG 0x001
|
||||
#define RGB_DEV_CONFIG_1_REG 0x002
|
||||
#define RGB_DEV_CONFIG_2_REG 0x003
|
||||
#define RGB_DEV_CONFIG_3_REG 0x004
|
||||
#define RGB_DEV_CONFIG_4_REG 0x005
|
||||
#define RGB_DEV_CONFIG_5_REG 0x006
|
||||
#define RGB_DEV_CONFIG_6_REG 0x007
|
||||
#define RGB_DEV_CONFIG_7_REG 0x008
|
||||
#define RGB_DEV_CONFIG_8_REG 0x009
|
||||
#define RGB_DEV_CONFIG_9_REG 0x00A
|
||||
#define RGB_DEV_CONFIG_10_REG 0x00B
|
||||
#define RGB_DEV_CONFIG_11_REG 0x00C
|
||||
#define RGB_DEV_CONFIG_12_REG 0x00D
|
||||
#define RGB_CMD_UPDATE_REG 0x010
|
||||
#define RGB_CMD_START_REG 0x011
|
||||
#define RGB_CMD_STOP_REG 0x012
|
||||
#define RGB_CMD_PAUSE_REG 0x013
|
||||
#define RGB_CMD_CONTINUE_REG 0x014
|
||||
#define RGB_LED_EN_1_REG 0x020
|
||||
#define RGB_LED_EN_2_REG 0x021
|
||||
#define RGB_FAULT_CLEAR_REG 0x022
|
||||
#define RGB_RESET_REG 0x023
|
||||
#define RGB_MANUAL_DC_BASE_REG 0x030
|
||||
|
||||
#define RGB_MANUAL_DC_4_REG 0x034
|
||||
#define RGB_MANUAL_DC_5_REG 0x035
|
||||
#define RGB_MANUAL_DC_6_REG 0x036
|
||||
#define RGB_MANUAL_DC_7_REG 0x037
|
||||
#define RGB_MANUAL_DC_8_REG 0x038
|
||||
#define RGB_MANUAL_DC_9_REG 0x039
|
||||
#define RGB_MANUAL_DC_10_REG 0x03A
|
||||
#define RGB_MANUAL_DC_11_REG 0x03B
|
||||
#define RGB_MANUAL_DC_12_REG 0x03C
|
||||
#define RGB_MANUAL_DC_13_REG 0x03D
|
||||
#define RGB_MANUAL_DC_14_REG 0x03E
|
||||
#define RGB_MANUAL_DC_15_REG 0x03F
|
||||
#define RGB_MANUAL_PWM_BASE_REG 0x040
|
||||
|
||||
#define RGB_MANUAL_PWM_4_REG 0x044
|
||||
#define RGB_MANUAL_PWM_5_REG 0x045
|
||||
#define RGB_MANUAL_PWM_6_REG 0x046
|
||||
#define RGB_MANUAL_PWM_7_REG 0x047
|
||||
#define RGB_MANUAL_PWM_8_REG 0x048
|
||||
#define RGB_MANUAL_PWM_9_REG 0x049
|
||||
#define RGB_MANUAL_PWM_10_REG 0x04A
|
||||
#define RGB_MANUAL_PWM_11_REG 0x04B
|
||||
#define RGB_MANUAL_PWM_12_REG 0x04C
|
||||
#define RGB_MANUAL_PWM_13_REG 0x04D
|
||||
#define RGB_MANUAL_PWM_14_REG 0x04E
|
||||
#define RGB_MANUAL_PWM_15_REG 0x04F
|
||||
|
||||
#define I2C_Timeout 1000
|
||||
|
||||
/**
|
||||
* Initialise the RGB led driver. This requires that the I2C has already been initialised
|
||||
*/
|
||||
void YEEP144_RGB_Init(void){
|
||||
|
||||
// Reset the LP5812 - 0x66 is magic number according to datasheet
|
||||
YEEP144_RGB_WriteReg(RGB_RESET_REG, 0x66);
|
||||
|
||||
// Enable the chip
|
||||
YEEP144_RGB_WriteReg(RGB_CHIP_EN_REG, 1);
|
||||
|
||||
// Set the scan mode - 24kHz PWM, scan drive mode with 4 scans, no direct drive outputs
|
||||
YEEP144_RGB_WriteReg(RGB_DEV_CONFIG_1_REG, 0x40);
|
||||
|
||||
// Set protection options - shutdown on open LED detected, shutdown on short LED detected, short threshold 0.65 * VCC
|
||||
YEEP144_RGB_WriteReg(RGB_DEV_CONFIG_12_REG, 0x0F);
|
||||
|
||||
// Apply updated configuration - 0x55 is magic number as per datasheet
|
||||
YEEP144_RGB_WriteReg(RGB_CMD_UPDATE_REG, 0x55);
|
||||
|
||||
// Set red current limits to 50% of max 25.2mA
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB1_RED , 0xFF / 2);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB2_RED , 0xFF / 2);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB3_RED , 0xFF / 2);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB4_RED , 0xFF / 2);
|
||||
|
||||
// Set green and blue current limits to 20% of max
|
||||
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB1_GREEN , 0xFF / 5);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB1_BLUE , 0xFF / 5);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB2_GREEN , 0xFF / 5);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB2_BLUE , 0xFF / 5);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB3_GREEN , 0xFF / 5);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB3_BLUE , 0xFF / 5);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB4_GREEN , 0xFF / 5);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_DC_BASE_REG + RGB4_BLUE , 0xFF / 5);
|
||||
|
||||
// Enable all the LEDS
|
||||
YEEP144_RGB_WriteReg(RGB_LED_EN_1_REG, 0xF0);
|
||||
YEEP144_RGB_WriteReg(RGB_LED_EN_2_REG, 0xFF);
|
||||
|
||||
// Set default colours
|
||||
YEEP144_RGB_SetRGB(1, RGB_GREEN);
|
||||
YEEP144_RGB_SetRGB(2, RGB_GOLD);
|
||||
YEEP144_RGB_SetRGB(3, RGB_RED);
|
||||
YEEP144_RGB_SetRGB(4, RGB_INDIGO);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Individually sets the brightness of an LED. Use the defines in the header file
|
||||
* to specify which LED e.g. RGB1_GREEN
|
||||
*/
|
||||
void YEEP144_RGB_SetLED(uint8_t led, uint8_t value){
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_PWM_BASE_REG + led, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the brightness for all 3 elements of an RGB led at one.
|
||||
* Instead of specifiying the RGB values, you can also substitute in one of the defines e.g. RGB_GOLD
|
||||
*/
|
||||
void YEEP144_RGB_SetRGB(uint8_t led, uint8_t red, uint8_t green, uint8_t blue){
|
||||
uint8_t offset = ((led - 1) * 3);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_PWM_BASE_REG + RGB1_RED + offset, red);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_PWM_BASE_REG + RGB1_GREEN + offset, green);
|
||||
YEEP144_RGB_WriteReg(RGB_MANUAL_PWM_BASE_REG + RGB1_BLUE + offset, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that writes a byte to the RGB controller at the specified register address
|
||||
*/
|
||||
void YEEP144_RGB_WriteReg(uint16_t reg, uint8_t data){
|
||||
// As there are so many registers in this chip, it uses a couple of the I2C address bits as part of the register address
|
||||
// This means we need to do some bit level manipulation to build our transmission address and data
|
||||
|
||||
uint8_t I2CAddress = RGB_ADDR + (reg >> 8); // set the bottom two bits of the address to the top two bits of the register
|
||||
|
||||
// Cast the register to a uint8_t so we just get the lowest 8 bits
|
||||
uint8_t reg8Bit = (uint8_t) reg;
|
||||
|
||||
// Pack out register address and data into an array for transmission
|
||||
uint8_t RGBCommand[2];
|
||||
RGBCommand[0] = reg8Bit;
|
||||
RGBCommand[1] = data;
|
||||
|
||||
// Send out the data. The constructed I2CAddress still needs to be shifted by 1 due to 7/8-bit addressing differences
|
||||
HAL_I2C_Master_Transmit(&hi2c2, I2CAddress << 1, RGBCommand, 2, I2C_Timeout);
|
||||
}
|
||||
779
YEEP144lib/src/YEEP144_SSD1306.c
Normal file
779
YEEP144lib/src/YEEP144_SSD1306.c
Normal file
@@ -0,0 +1,779 @@
|
||||
// This code is largely inspired by the Adafruit SSD1306 and Adafruit GFX Arduino libraries
|
||||
// re-written to simplify it down to plain C rather than object based C++, optimise for STM32
|
||||
// rather than Arduino, and to focus on specifically the monochrome SSD1306 displays rather than
|
||||
// a general purpose graphics library
|
||||
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_i2c.h"
|
||||
#include "YEEP144_I2C.h"
|
||||
#include "YEEP144_SSD1306.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <strings.h>
|
||||
|
||||
#define WIDTH 128
|
||||
#define HEIGHT 32
|
||||
#define SSD1306_ADDR 0x3C << 1
|
||||
#define I2C_TIMEOUT 1000
|
||||
|
||||
#define FONTWIDTH 5
|
||||
#define FONTHEIGHT 7
|
||||
#define MAXCHAR ((WIDTH / (FONTWIDTH + 1)) * (HEIGHT / (FONTHEIGHT + 1)))
|
||||
|
||||
#define SSD1306_MEMORYMODE 0x20
|
||||
#define SSD1306_COLUMNADDR 0x21
|
||||
#define SSD1306_PAGEADDR 0x22
|
||||
#define SSD1306_SETCONTRAST 0x81
|
||||
#define SSD1306_CHARGEPUMP 0x8D
|
||||
#define SSD1306_SEGREMAP 0xA0
|
||||
#define SSD1306_DISPLAYALLON_RESUME 0xA4
|
||||
#define SSD1306_DISPLAYALLON 0xA5
|
||||
#define SSD1306_NORMALDISPLAY 0xA6
|
||||
#define SSD1306_INVERTDISPLAY 0xA7
|
||||
#define SSD1306_SETMULTIPLEX 0xA8
|
||||
#define SSD1306_DISPLAYOFF 0xAE
|
||||
#define SSD1306_DISPLAYON 0xAF
|
||||
#define SSD1306_COMSCANINC 0xC0
|
||||
#define SSD1306_COMSCANDEC 0xC8
|
||||
#define SSD1306_SETDISPLAYOFFSET 0xD3
|
||||
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
|
||||
#define SSD1306_SETPRECHARGE 0xD9
|
||||
#define SSD1306_SETCOMPINS 0xDA
|
||||
#define SSD1306_SETVCOMDETECT 0xDB
|
||||
|
||||
#define SSD1306_SETLOWCOLUMN 0x00
|
||||
#define SSD1306_SETHIGHCOLUMN 0x10
|
||||
#define SSD1306_SETSTARTLINE 0x40
|
||||
|
||||
#define SSD1306_EXTERNALVCC 0x01
|
||||
#define SSD1306_SWITCHCAPVCC 0x02
|
||||
|
||||
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
|
||||
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
|
||||
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
|
||||
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A
|
||||
#define SSD1306_DEACTIVATE_SCROLL 0x2E
|
||||
#define SSD1306_ACTIVATE_SCROLL 0x2F
|
||||
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3
|
||||
|
||||
uint8_t *displayTXBuffer;
|
||||
uint8_t *displayBuffer;
|
||||
|
||||
uint8_t cursorX = 0;
|
||||
uint8_t cursorY = 0;
|
||||
uint8_t textSize = 1;
|
||||
uint8_t textColour = SSD1306_WHITE;
|
||||
uint8_t textBGColour = SSD1306_BLACK;
|
||||
uint8_t textWrap = 1;
|
||||
|
||||
void YEEP144_SSD1306_DrawFastHLine(uint8_t x, uint8_t y, int8_t w, uint8_t colour);
|
||||
void YEEP144_SSD1306_DrawFastVLine(uint8_t x, uint8_t y, int8_t h, uint8_t colour);
|
||||
void YEEP144_SSD1306_DrawLineInternal(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t colour);
|
||||
|
||||
const unsigned char UOYLogo [] = {
|
||||
0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x60, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xe0, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0xc8, 0x18, 0x0d, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x3c, 0x04, 0x00, 0x40, 0x69, 0x24, 0xd7, 0xb8, 0xcd, 0xe9, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0xbc, 0x00, 0x10, 0x00, 0x49, 0xa4, 0x02, 0x25, 0x08, 0x86, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0xdc, 0x01, 0x16, 0x40, 0x49, 0x64, 0x42, 0x28, 0xc8, 0x84, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xff, 0x8f, 0xe8, 0x00, 0x49, 0x24, 0x62, 0xad, 0x48, 0x84, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3e, 0x77, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7e, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0xf6, 0x0d, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xe0, 0x1c, 0xf8, 0x00, 0x00, 0x0c, 0x38, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0xc0, 0x86, 0xff, 0x80, 0x00, 0x14, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0xef, 0x9f, 0xfb, 0x00, 0x00, 0x20, 0x18, 0x10, 0x00, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08, 0xa0, 0xfe, 0x00, 0x1e, 0x20, 0x10, 0x60, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x58, 0xb6, 0x00, 0x27, 0xf8, 0x30, 0xe0, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xb8, 0x3c, 0x33, 0x00, 0x4c, 0x40, 0x31, 0xc0, 0x00, 0x60, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x98, 0x7e, 0x33, 0x00, 0x84, 0xc0, 0x63, 0x86, 0x30, 0x40, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xf8, 0xc2, 0x10, 0x00, 0x88, 0x80, 0x6b, 0x12, 0xb7, 0xc3, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x69, 0xdb, 0x1a, 0x00, 0x90, 0x80, 0xd6, 0x22, 0x38, 0x86, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x79, 0xff, 0x90, 0x00, 0xe1, 0x00, 0xec, 0x42, 0x31, 0x98, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7b, 0xff, 0xd0, 0x00, 0x02, 0x01, 0x98, 0xc4, 0x61, 0xe0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x6c, 0x3c, 0x2c, 0x00, 0x04, 0x01, 0x30, 0x8c, 0x43, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x04, 0xa4, 0xa0, 0x00, 0x00, 0x00, 0x70, 0x98, 0x82, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x07, 0xc3, 0xe0, 0x00, 0x00, 0x00, 0x60, 0xe0, 0x84, 0x0f, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x07, 0x99, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x03, 0x11, 0xc0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x18, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x80, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x40, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const unsigned char font[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B,
|
||||
0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C,
|
||||
0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00,
|
||||
0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24,
|
||||
0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E,
|
||||
0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F,
|
||||
0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C,
|
||||
0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F,
|
||||
0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89,
|
||||
0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94,
|
||||
0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08,
|
||||
0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10,
|
||||
0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x30, 0x38, 0x3E, 0x38, 0x30, 0x06,
|
||||
0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F,
|
||||
0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14,
|
||||
0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49,
|
||||
0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41,
|
||||
0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08,
|
||||
0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02,
|
||||
0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49,
|
||||
0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F,
|
||||
0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41,
|
||||
0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49,
|
||||
0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00,
|
||||
0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41,
|
||||
0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59,
|
||||
0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E,
|
||||
0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49,
|
||||
0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73,
|
||||
0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40,
|
||||
0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40,
|
||||
0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E,
|
||||
0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51,
|
||||
0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32,
|
||||
0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20,
|
||||
0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14,
|
||||
0x63, 0x03, 0x04, 0x78, 0x04, 0x03, 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00,
|
||||
0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41,
|
||||
0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40,
|
||||
0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28,
|
||||
0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28,
|
||||
0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18,
|
||||
0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D,
|
||||
0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00,
|
||||
0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08,
|
||||
0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24,
|
||||
0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48,
|
||||
0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40,
|
||||
0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C,
|
||||
0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64,
|
||||
0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00,
|
||||
0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C,
|
||||
0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40,
|
||||
0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41,
|
||||
0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
|
||||
0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E,
|
||||
0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54,
|
||||
0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00,
|
||||
0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11,
|
||||
0x12, 0x7D, // A-umlaut
|
||||
0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54,
|
||||
0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49,
|
||||
0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
|
||||
0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42,
|
||||
0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42,
|
||||
0x3D, // O-umlaut
|
||||
0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E,
|
||||
0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6,
|
||||
0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00,
|
||||
0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40,
|
||||
0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D,
|
||||
0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48,
|
||||
0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00,
|
||||
0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A,
|
||||
0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old
|
||||
// code
|
||||
0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
|
||||
0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14,
|
||||
0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10,
|
||||
0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00,
|
||||
0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17,
|
||||
0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00,
|
||||
0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10,
|
||||
0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00,
|
||||
0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F,
|
||||
0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17,
|
||||
0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14,
|
||||
0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17,
|
||||
0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x10,
|
||||
0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00,
|
||||
0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0,
|
||||
0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10,
|
||||
0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44,
|
||||
0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
|
||||
0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55,
|
||||
0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E,
|
||||
0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C,
|
||||
0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D,
|
||||
0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D,
|
||||
0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A,
|
||||
0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44,
|
||||
0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0,
|
||||
0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36,
|
||||
0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00,
|
||||
0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F,
|
||||
0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C,
|
||||
0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the SSD1306 OLED display. This requires that the I2C has already been initialised.
|
||||
*/
|
||||
void YEEP144_SSD1306_Init(void){
|
||||
|
||||
// Allocate space in memory for the framebuffer to be stored
|
||||
// As the display is monochrome we only need 1 bit per pixel, so the data is packed
|
||||
// to save on memory space - i.e. 8 pixels per byte
|
||||
// The arithmetic used ensures that the buffer size is always padded to a whole byte number
|
||||
//
|
||||
// This is also how the framebuffer is stored within the OLED chip, so it can be directly
|
||||
// copied across without additional manipulation
|
||||
//
|
||||
// We make the TX buffer 1 byte larger than needed for the display data so the value 0x40 can be prepended
|
||||
// When it comes time to send the framebuffer to the display, that initial byte is required so the data is treated
|
||||
// as display data, not commands
|
||||
displayTXBuffer = (uint8_t *)malloc((WIDTH * ((HEIGHT + 7) / 8)) + 1);
|
||||
displayTXBuffer[0] = 0x40;
|
||||
|
||||
// For programming ease we store a separate pointer offset by 1 to directly access the display data portion of the buffer
|
||||
displayBuffer = displayTXBuffer + 1;
|
||||
|
||||
// Ensure the display buffer is empty
|
||||
YEEP144_SSD1306_ClearDisplay();
|
||||
|
||||
// This list of commands sets up the display and turns it on
|
||||
uint8_t initCommandList[] = {
|
||||
0x00, // Tell the display these are commands not graphics data
|
||||
SSD1306_DISPLAYOFF,
|
||||
SSD1306_SETDISPLAYCLOCKDIV,
|
||||
0x80, // Recommended clock div
|
||||
SSD1306_SETMULTIPLEX,
|
||||
HEIGHT - 1,
|
||||
SSD1306_SETDISPLAYOFFSET,
|
||||
0x0, // No display offset
|
||||
SSD1306_SETSTARTLINE | 0x0, // Start at line 0
|
||||
SSD1306_CHARGEPUMP,
|
||||
0x14, // Enable internal charge pump
|
||||
SSD1306_MEMORYMODE,
|
||||
0x00,
|
||||
SSD1306_SEGREMAP | 0x1,
|
||||
SSD1306_COMSCANDEC,
|
||||
SSD1306_SETCOMPINS,
|
||||
0x02, // This is hardcoded for the value for a 128x32 screen
|
||||
SSD1306_SETCONTRAST,
|
||||
0x8F, // This is hardcoded for the value for a 128x32 screen
|
||||
SSD1306_SETPRECHARGE,
|
||||
0xF1, // Value for when charge pump is enabled
|
||||
SSD1306_SETVCOMDETECT,
|
||||
0x40,
|
||||
SSD1306_DISPLAYALLON_RESUME,
|
||||
SSD1306_NORMALDISPLAY,
|
||||
SSD1306_DEACTIVATE_SCROLL,
|
||||
SSD1306_DISPLAYON
|
||||
|
||||
};
|
||||
|
||||
// Send the commands
|
||||
HAL_I2C_Master_Transmit(&hi2c2, SSD1306_ADDR, initCommandList, sizeof(initCommandList), I2C_TIMEOUT);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the display by transfering the contents of the display buffer to the display.
|
||||
* No changes to the displayed graphics will be made until this command is called
|
||||
*/
|
||||
void YEEP144_SSD1306_Display(void){
|
||||
// Before sending graphics data we need to define where the data goes
|
||||
// As we are always copying over the whole frame buffer, we want our transfer to fill
|
||||
// the whole graphics memory on the driver chip
|
||||
uint8_t commandList[] = {
|
||||
0x00, // Following data are commands
|
||||
SSD1306_PAGEADDR,
|
||||
0, // Starting page
|
||||
0xFF, // Ending page
|
||||
SSD1306_COLUMNADDR,
|
||||
0, // Starting column
|
||||
(WIDTH-1), // Ending column
|
||||
//0x40 // Following data will be graphics not commands
|
||||
};
|
||||
|
||||
// Send these commands
|
||||
HAL_I2C_Master_Transmit(&hi2c2, SSD1306_ADDR, commandList, sizeof(commandList), I2C_TIMEOUT);
|
||||
|
||||
// Send the graphics data. We use the TXbuffer here so it includes the additional byte at the start that tells the chip this is graphics data
|
||||
HAL_I2C_Master_Transmit(&hi2c2, SSD1306_ADDR, displayTXBuffer, (WIDTH * ((HEIGHT + 7) / 8)) + 1, I2C_TIMEOUT);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the display by clearing the display buffer
|
||||
*/
|
||||
void YEEP144_SSD1306_ClearDisplay(void){
|
||||
|
||||
// Clear the display by filling the buffer with all zeros
|
||||
memset(displayBuffer, 0, WIDTH * ((HEIGHT + 7) / 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a single pixel in the display buffer to the specified colour
|
||||
*/
|
||||
void YEEP144_SSD1306_SetPixel(uint8_t x, uint8_t y, uint8_t colour){
|
||||
|
||||
// As the data is packed with 8 pixels per byte, we need to do some bit-level
|
||||
// manipulation to set an individual pixel
|
||||
switch(colour){
|
||||
// Set the bit for white (on)
|
||||
case SSD1306_WHITE:
|
||||
displayBuffer[x + (y / 8) * WIDTH] |= (1 << (y & 7));
|
||||
break;
|
||||
// Clear the bit for black (off)
|
||||
case SSD1306_BLACK:
|
||||
displayBuffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7));
|
||||
break;
|
||||
// Invert the bit to invert the colour
|
||||
case SSD1306_INVERSE:
|
||||
displayBuffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the specified pixel from the display buffer.
|
||||
* Note that this is what is currently stored in the local buffer, not
|
||||
* necessarily what is currently being displayed if Display has not been called
|
||||
*/
|
||||
bool YEEP144_SSD1306_GetPixel(uint8_t x, uint8_t y){
|
||||
// As the 1-bit pixels are packed into bytes, we need to do some bit manipulation to retrieve the individual bit
|
||||
return (displayBuffer[x + (y / 8) * WIDTH] & (1 << (y & 7)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a line between the specified coordinates, using the specified colour
|
||||
*/
|
||||
void YEEP144_SSD1306_DrawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t colour){
|
||||
// This function checks if the draw can be fufilled by one of the optimised horizontal or vertical
|
||||
// line drawing functions, if not passes to the generalised one
|
||||
if(x0 == x1){ // If both x coordinates are the same then this is vertical
|
||||
if (y0 > y1){ // Normalise direction by swapping the coords if needed
|
||||
uint8_t t = y0;
|
||||
y0 = y1;
|
||||
y1 = t;
|
||||
}
|
||||
YEEP144_SSD1306_DrawFastVLine(x0, y0, y1 - y0, colour);
|
||||
}
|
||||
else if(y0 == y1){ // If both y coordinates are the same then this is horizontal
|
||||
if(x0 > x1){ // Normalise direction by swapping the coords if needed
|
||||
uint8_t t = x0;
|
||||
x0 = x1;
|
||||
x1 = t;
|
||||
}
|
||||
YEEP144_SSD1306_DrawFastHLine(x0, y0, x1 - x0, colour);
|
||||
}
|
||||
else{
|
||||
// If this cannot be handled by either of the fast algorithms, fallback to the general one
|
||||
YEEP144_SSD1306_DrawLineInternal(x0, y0, x1, y1, colour);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a line between the two specified points using Bresenham's algorithm
|
||||
* This is only used when the line is not vertical or horizontal
|
||||
* This will only be an approximation of requested line due to the limited resolution of the display
|
||||
* and the lack of options for antialiasing
|
||||
*/
|
||||
void YEEP144_SSD1306_DrawLineInternal(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t colour){
|
||||
// Bresenham's algorithm only works when going in the positive direction, which would be down and right
|
||||
// for a display with (0,0) in the top left corner. So that this can be used for all cases,
|
||||
// we need to detect the direction of the requested line and transform the coordinates
|
||||
uint8_t steep = abs(y1 - y0) > abs(x1 - x0);
|
||||
|
||||
if(steep){
|
||||
uint8_t t = x0;
|
||||
x0 = y0;
|
||||
y0 = t;
|
||||
|
||||
t = x1;
|
||||
x1 = y1;
|
||||
y1 = t;
|
||||
}
|
||||
|
||||
if(x0 > x1){
|
||||
uint8_t t = x0;
|
||||
x0 = x1;
|
||||
x1 = t;
|
||||
t = y0;
|
||||
y0 = y1;
|
||||
y1 = t;
|
||||
}
|
||||
|
||||
int8_t dx = x1 - x0;
|
||||
int8_t dy = abs(y1 - y0);
|
||||
|
||||
int8_t err = dx / 2;
|
||||
int8_t ystep;
|
||||
|
||||
if(y0 < y1){
|
||||
ystep = 1;
|
||||
}
|
||||
else{
|
||||
ystep = -1;
|
||||
}
|
||||
|
||||
for(; x0 <= x1; x0++){
|
||||
if(steep){
|
||||
YEEP144_SSD1306_SetPixel(y0, x0, colour);
|
||||
}
|
||||
else{
|
||||
YEEP144_SSD1306_SetPixel(x0, y0, colour);
|
||||
}
|
||||
err -= dy;
|
||||
if(err < 0){
|
||||
y0 += ystep;
|
||||
err += dx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a purely horizontal line. This is faster than the general algorithm,
|
||||
* but only works for horizontal lines
|
||||
*/
|
||||
void YEEP144_SSD1306_DrawFastHLine(uint8_t x, uint8_t y, int8_t w, uint8_t colour){
|
||||
if (y < HEIGHT) { // Y coord in bounds?
|
||||
if ((x + w) > WIDTH) { // Clip right
|
||||
w = (WIDTH - x);
|
||||
}
|
||||
if (w > 0) { // Proceed only if width is positive
|
||||
uint8_t *pBuf = &displayBuffer[(y / 8) * WIDTH + x];
|
||||
uint8_t mask = 1 << (y & 7);
|
||||
switch (colour) {
|
||||
case SSD1306_WHITE:
|
||||
while (w--) {
|
||||
*pBuf++ |= mask;
|
||||
};
|
||||
break;
|
||||
case SSD1306_BLACK:
|
||||
mask = ~mask;
|
||||
while (w--) {
|
||||
*pBuf++ &= mask;
|
||||
};
|
||||
break;
|
||||
case SSD1306_INVERSE:
|
||||
while (w--) {
|
||||
*pBuf++ ^= mask;
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a purely vertical line. This is faster than the general algorithm,
|
||||
* but only works for vertical lines
|
||||
*/
|
||||
void YEEP144_SSD1306_DrawFastVLine(uint8_t x, uint8_t y, int8_t h, uint8_t colour){
|
||||
if (x < WIDTH) { // X coord in bounds?
|
||||
if ((y + h) > HEIGHT) { // Clip bottom
|
||||
h = (HEIGHT - y);
|
||||
}
|
||||
if (h > 0) { // Proceed only if height is now positive
|
||||
|
||||
uint8_t *pBuf = &displayBuffer[(y / 8) * WIDTH + x];
|
||||
|
||||
// do the first partial byte, if necessary - this requires some masking
|
||||
uint8_t mod = (y & 7);
|
||||
if (mod) {
|
||||
// mask off the high n bits we want to set
|
||||
mod = 8 - mod;
|
||||
// note - lookup table results in a nearly 10% performance
|
||||
// improvement in fill* functions
|
||||
// uint8_t mask = ~(0xFF >> mod);
|
||||
static const uint8_t premask[8] = {0x00, 0x80, 0xC0, 0xE0,
|
||||
0xF0, 0xF8, 0xFC, 0xFE};
|
||||
uint8_t mask = premask[mod];
|
||||
// adjust the mask if we're not going to reach the end of this byte
|
||||
if (h < mod)
|
||||
mask &= (0XFF >> (mod - h));
|
||||
|
||||
switch (colour) {
|
||||
case SSD1306_WHITE:
|
||||
*pBuf |= mask;
|
||||
break;
|
||||
case SSD1306_BLACK:
|
||||
*pBuf &= ~mask;
|
||||
break;
|
||||
case SSD1306_INVERSE:
|
||||
*pBuf ^= mask;
|
||||
break;
|
||||
}
|
||||
pBuf += WIDTH;
|
||||
}
|
||||
|
||||
if (h >= mod) { // More to go?
|
||||
h -= mod;
|
||||
// Write solid bytes while we can - effectively 8 rows at a time
|
||||
if (h >= 8) {
|
||||
if (colour == SSD1306_INVERSE) {
|
||||
// separate copy of the code so we don't impact performance of
|
||||
// black/white write version with an extra comparison per loop
|
||||
do {
|
||||
*pBuf ^= 0xFF; // Invert byte
|
||||
pBuf += WIDTH; // Advance pointer 8 rows
|
||||
h -= 8; // Subtract 8 rows from height
|
||||
} while (h >= 8);
|
||||
} else {
|
||||
// store a local value to work with
|
||||
uint8_t val = (colour != SSD1306_BLACK) ? 255 : 0;
|
||||
do {
|
||||
*pBuf = val; // Set byte
|
||||
pBuf += WIDTH; // Advance pointer 8 rows
|
||||
h -= 8; // Subtract 8 rows from height
|
||||
} while (h >= 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (h) { // Do the final partial byte, if necessary
|
||||
mod = h & 7;
|
||||
// this time we want to mask the low bits of the byte,
|
||||
// vs the high bits we did above
|
||||
// uint8_t mask = (1 << mod) - 1;
|
||||
// note - lookup table results in a nearly 10% performance
|
||||
// improvement in fill* functions
|
||||
static const uint8_t postmask[8] = {0x00, 0x01, 0x03, 0x07,
|
||||
0x0F, 0x1F, 0x3F, 0x7F};
|
||||
uint8_t mask = postmask[mod];
|
||||
switch (colour) {
|
||||
case SSD1306_WHITE:
|
||||
*pBuf |= mask;
|
||||
break;
|
||||
case SSD1306_BLACK:
|
||||
*pBuf &= ~mask;
|
||||
break;
|
||||
case SSD1306_INVERSE:
|
||||
*pBuf ^= mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // endif positive height
|
||||
} // endif x in bounds
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws an outline of a rectangle with no fill. Coordinates represent the top left corner
|
||||
*/
|
||||
void YEEP144_SSD1306_DrawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t colour){
|
||||
YEEP144_SSD1306_DrawFastHLine(x, y, w, colour);
|
||||
YEEP144_SSD1306_DrawFastHLine(x, y + h - 1, w, colour);
|
||||
YEEP144_SSD1306_DrawFastVLine(x, y, h, colour);
|
||||
YEEP144_SSD1306_DrawFastVLine(x + w - 1, y, h, colour);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a filled rectangle of given width and height, with the top left corner specified
|
||||
* by the given coordinates
|
||||
*/
|
||||
void YEEP144_SSD1306_DrawFillRect(uint8_t x, uint8_t y, int8_t w, int8_t h, uint8_t colour){
|
||||
for (int16_t i = x; i < x + w; i++) {
|
||||
YEEP144_SSD1306_DrawFastVLine(i, y, h, colour);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints text to the screen using printf-style formatting. Cursor starting position, font size and
|
||||
* text wrapping can be controlled using the provided functions
|
||||
*/
|
||||
int YEEP144_SSD1306_printf(const char * format, ...){
|
||||
// Set the buffer size to the maximum that can fit on the screen with the smallest font
|
||||
size_t bufSize = MAXCHAR;
|
||||
|
||||
// Try to allocate that buffer
|
||||
char* buffer = malloc(bufSize);
|
||||
|
||||
// If the buffer cannot be allocated, return -1
|
||||
if (buffer == NULL) return -1;
|
||||
|
||||
// Get the pointer to the variadic args
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
|
||||
// Create our final string in the buffer using vsnprintf, passing through our argument pointer
|
||||
vsnprintf(buffer, bufSize, format, args);
|
||||
|
||||
// Calulate the final size of the string
|
||||
bufSize = strlen(buffer);
|
||||
|
||||
// Write our characters one by one to the screen
|
||||
for(size_t i = 0; i < bufSize; i++){
|
||||
char c = buffer[i];
|
||||
|
||||
if(c == '\n'){// Newline - move cursor down by 1 line, and back to start of line
|
||||
cursorX = 0;
|
||||
cursorY += textSize * (FONTHEIGHT + 1);
|
||||
}
|
||||
else if(c != '\r'){ // All other text - ignoring carriage returns
|
||||
if(textWrap && ((cursorX + textSize * (FONTWIDTH + 1)) > WIDTH)){
|
||||
// If wrapping is enabled and this character will overflow the display, move to start of next line
|
||||
cursorX = 0;
|
||||
cursorY += textSize * (FONTHEIGHT + 1);
|
||||
}
|
||||
if((cursorY + FONTHEIGHT) > HEIGHT){ // If we have reached the bottom of the screen, start back at the top
|
||||
cursorY = 0;
|
||||
cursorX = 0;
|
||||
}
|
||||
// Draw the character at the cursor coordinates
|
||||
YEEP144_SSD1306_DrawChar(cursorX, cursorY, c, textColour, textBGColour, textSize);
|
||||
|
||||
// Move the cursor forward by one character
|
||||
cursorX += (FONTWIDTH + 1) * textSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Free the memory used
|
||||
free(buffer);
|
||||
|
||||
// Return number of characters printed
|
||||
return bufSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a single character to the screen, with the specified coordinates being the top left of the character
|
||||
* Uses the specified text colour, background colour and size multiplier
|
||||
*/
|
||||
void YEEP144_SSD1306_DrawChar(uint8_t x, uint8_t y, uint8_t c, uint8_t colour, uint8_t bg, uint8_t size){
|
||||
|
||||
if ((x >= WIDTH) || // Clip right
|
||||
(y >= HEIGHT) || // Clip bottom
|
||||
((x + 6 * size - 1) < 0) || // Clip left
|
||||
((y + 8 * size - 1) < 0)) // Clip top
|
||||
return;
|
||||
|
||||
for (int8_t i = 0; i < 5; i++) { // Char bitmap = 5 columns
|
||||
uint8_t line = font[c * 5 + i];
|
||||
for (int8_t j = 0; j < 8; j++, line >>= 1) {
|
||||
if (line & 1) {
|
||||
if (size == 1)
|
||||
YEEP144_SSD1306_SetPixel(x + i, y + j, colour);
|
||||
else
|
||||
YEEP144_SSD1306_DrawFillRect(x + i * size, y + j * size, size, size, colour);
|
||||
} else if (bg != colour) {
|
||||
if (size == 1)
|
||||
YEEP144_SSD1306_SetPixel(x + i, y + j, bg);
|
||||
else
|
||||
YEEP144_SSD1306_DrawFillRect(x + i * size, y + j * size, size, size, bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bg != colour) { // If opaque, draw vertical line for last column
|
||||
if (size == 1)
|
||||
YEEP144_SSD1306_DrawFastVLine(x + 5, y, 8, bg);
|
||||
else
|
||||
YEEP144_SSD1306_DrawFillRect(x + 5 * size, y, size, 8 * size, bg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text scaling.
|
||||
* 1 = Normal, 4 lines will fit on display
|
||||
* 2 = 2 lines will fit
|
||||
* 4 = 1 line will fit
|
||||
*/
|
||||
void YEEP144_SSD1306_SetTextSize(uint8_t size){
|
||||
textSize = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position of the cursor
|
||||
* The coordinates defire the top left corner of the character
|
||||
*/
|
||||
void YEEP144_SSD1306_SetTextCursor(uint8_t x, uint8_t y){
|
||||
cursorX = x;
|
||||
cursorY = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable text line wrapping
|
||||
* 0 is disabled, 1 is enabled
|
||||
*/
|
||||
void YEEP144_SSD1306_SetTextWrap(uint8_t wrap){
|
||||
textWrap = wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a 1-bit bitmap to the screen at the specified position
|
||||
*/
|
||||
void YEEP144_SSD1306_DrawBitmap(uint8_t x, uint8_t y, const uint8_t bitmap[], uint8_t w, uint8_t h){
|
||||
uint8_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
|
||||
uint8_t b = 0;
|
||||
|
||||
for (uint8_t j = 0; j < h; j++, y++) {
|
||||
for (uint8_t i = 0; i < w; i++) {
|
||||
if (i & 7){
|
||||
b <<= 1;
|
||||
}
|
||||
else{
|
||||
b = bitmap[j * byteWidth + i / 8];
|
||||
}
|
||||
YEEP144_SSD1306_SetPixel(x + i, y, (b & 0x80) ? SSD1306_WHITE : SSD1306_BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverts the colours of the display. Note that this happens using a hardware command to the display
|
||||
* and doesn't touch the buffer
|
||||
*/
|
||||
void YEEP144_SSD1306_InvertDisplay(bool i){
|
||||
YEEP144_SSD1306_SendCommand(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that sends a single command to the display
|
||||
*/
|
||||
void YEEP144_SSD1306_SendCommand(uint8_t command){
|
||||
|
||||
// For data to be recognised as a command, the I2C transaction must start with 0x00
|
||||
uint8_t data[] = {0x00, command};
|
||||
|
||||
// Transmit the data
|
||||
HAL_I2C_Master_Transmit(&hi2c2, SSD1306_ADDR, data, 2, I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
/*
|
||||
Much of this code was taken from or inspired by the Adafruit_SSD1306 and Adafruit_GFX libraries,
|
||||
adapted for use on STM32 rather than the Arduino platform. For that code the following disclaimer applies:
|
||||
|
||||
Copyright (c) 2013 Adafruit Industries. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
313
YEEP144lib/src/YEEP144_Servo.c
Normal file
313
YEEP144lib/src/YEEP144_Servo.c
Normal file
@@ -0,0 +1,313 @@
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_Servo.h"
|
||||
|
||||
#include "YEEP144_Pinout.h"
|
||||
#include "stm32h563xx.h"
|
||||
#include "stm32h5xx_hal.h"
|
||||
#include "stm32h5xx_hal_gpio.h"
|
||||
#include "stm32h5xx_hal_gpio_ex.h"
|
||||
#include "stm32h5xx_hal_rcc.h"
|
||||
#include "stm32h5xx_hal_tim.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
TIM_HandleTypeDef htim5;
|
||||
TIM_HandleTypeDef htim13;
|
||||
TIM_HandleTypeDef htim17servo;
|
||||
|
||||
uint16_t servoMinTicks[4] = {2048, 2048, 2048, 2048};
|
||||
uint16_t servoMaxTicks[4] = {4095, 4095, 4095, 4095};
|
||||
float servoMinDegrees[4] = {0, 0, 0, 0};
|
||||
float servoMaxDegrees[4] = {180, 180, 180, 180};
|
||||
|
||||
uint16_t YEEP144_Servo_MapAngleToTicks(float angle, float minAngle, float maxAngle, uint16_t minTicks, uint16_t maxTicks);
|
||||
|
||||
/**
|
||||
* Initialises servos 1 and 2. This consumes timer 5 and two GPIO
|
||||
*/
|
||||
void YEEP144_Servo_Init1_2(void){
|
||||
|
||||
// Enable clock to TIM5
|
||||
__HAL_RCC_TIM5_CLK_ENABLE();
|
||||
|
||||
// Configure and initialise TIM5
|
||||
// Servo PWM is a 1ms - 2ms pulse every 20ms
|
||||
// This is done using a 50Hz PWM with a duty cycle between 5 and 10%
|
||||
// Our ARR is set to 40950, the range of the ADC is 0-4095, /2 = 2048, which represents 5% of the total range
|
||||
// Offsetting by 2048 means we cover the 5 to 10% range
|
||||
TIM_MasterConfigTypeDef sMasterConfig = {0};
|
||||
TIM_OC_InitTypeDef sConfigOC = {0};
|
||||
|
||||
htim5.Instance = TIM5;
|
||||
htim5.Init.Prescaler = 121;
|
||||
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim5.Init.Period = 40950;
|
||||
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
if (HAL_TIM_PWM_Init(&htim5) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Disable synchronisation as it is not needed
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Configure the two used PWM channels
|
||||
sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
||||
sConfigOC.Pulse = 0;
|
||||
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
if (HAL_TIM_PWM_ConfigChannel(&htim5, &sConfigOC, TIM_CHANNEL_1) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
if (HAL_TIM_PWM_ConfigChannel(&htim5, &sConfigOC, TIM_CHANNEL_4) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Configure GPIOs
|
||||
YEEP144_GPIO_InitAdv(SERVO1_PORT, SERVO1_PIN | SERVO2_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF2_TIM5);
|
||||
|
||||
HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_1);
|
||||
HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_4);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises servo 3. This consumes timer 13 and one GPIO
|
||||
*/
|
||||
void YEEP144_Servo_Init3(void){
|
||||
// Enable clock to TIM13
|
||||
__HAL_RCC_TIM13_CLK_ENABLE();
|
||||
|
||||
// Configure and initialise TIM13
|
||||
// Servo PWM is a 1ms - 2ms pulse every 20ms
|
||||
// This is done using a 50Hz PWM with a duty cycle between 5 and 10%
|
||||
// Our ARR is set to 40950, the range of the ADC is 0-4095, /2 = 2048, which represents 5% of the total range
|
||||
// Offsetting by 2048 means we cover the 5 to 10% range
|
||||
TIM_MasterConfigTypeDef sMasterConfig = {0};
|
||||
TIM_OC_InitTypeDef sConfigOC = {0};
|
||||
|
||||
htim13.Instance = TIM13;
|
||||
htim13.Init.Prescaler = 121;
|
||||
htim13.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim13.Init.Period = 40950;
|
||||
htim13.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
htim13.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
if (HAL_TIM_PWM_Init(&htim13) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Disable synchronisation as it is not needed
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if (HAL_TIMEx_MasterConfigSynchronization(&htim13, &sMasterConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Configure the two used PWM channels
|
||||
sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
||||
sConfigOC.Pulse = 0;
|
||||
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
if (HAL_TIM_PWM_ConfigChannel(&htim13, &sConfigOC, TIM_CHANNEL_1) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Configure GPIOs
|
||||
YEEP144_GPIO_InitAdv(SERVO3_PORT, SERVO3_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF9_TIM13);
|
||||
|
||||
HAL_TIM_PWM_Start(&htim13, TIM_CHANNEL_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises servo 4. This consumes timer 17 and one GPIO. These resources are share with the buzzer.
|
||||
* Ensure only one is initialised. When using servo 4, disable the buzzer using DIP switch SW5 so the
|
||||
* PWM signal is not made audible
|
||||
*/
|
||||
void YEEP144_Servo_Init4(void){
|
||||
// Enable clock to TIM17
|
||||
__HAL_RCC_TIM17_CLK_ENABLE();
|
||||
|
||||
// Configure and initialise TIM17
|
||||
// Servo PWM is a 1ms - 2ms pulse every 20ms
|
||||
// This is done using a 50Hz PWM with a duty cycle between 5 and 10%
|
||||
// Our ARR is set to 40950, the range of the ADC is 0-4095, /2 = 2048, which represents 5% of the total range
|
||||
// Offsetting by 2048 means we cover the 5 to 10% range
|
||||
TIM_MasterConfigTypeDef sMasterConfig = {0};
|
||||
TIM_OC_InitTypeDef sConfigOC = {0};
|
||||
|
||||
htim17servo.Instance = TIM17;
|
||||
htim17servo.Init.Prescaler = 121;
|
||||
htim17servo.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim17servo.Init.Period = 40950;
|
||||
htim17servo.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
htim17servo.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
if (HAL_TIM_PWM_Init(&htim17servo) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Disable synchronisation as it is not needed
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if (HAL_TIMEx_MasterConfigSynchronization(&htim17servo, &sMasterConfig) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Configure the two used PWM channels
|
||||
sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
||||
sConfigOC.Pulse = 0;
|
||||
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
if (HAL_TIM_PWM_ConfigChannel(&htim17servo, &sConfigOC, TIM_CHANNEL_1) != HAL_OK){
|
||||
//Error_Handler();
|
||||
}
|
||||
|
||||
// Configure GPIOs
|
||||
YEEP144_GPIO_InitAdv(SERVO4_PORT, SERVO4_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF1_TIM17);
|
||||
|
||||
HAL_TIM_PWM_Start(&htim17servo, TIM_CHANNEL_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the zero point for a given servo
|
||||
*
|
||||
* Default configuration is 2048, yielding a 5% to 10% duty cycle, or 1ms - 2ms pulse, which is the typical standard
|
||||
* Adjusting the zero offset can help account for differences in servos
|
||||
* that means 5% duty cycle does not exactly reach 0 degrees
|
||||
*
|
||||
* The offset applies to both SetDirect and SetAngle
|
||||
*/
|
||||
void YEEP144_Servo_SetMinTicks(uint8_t servo, uint16_t min){
|
||||
if(servo == 0 || servo >= 4) return;
|
||||
servoMinTicks[servo-1] = min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max point for a given servo
|
||||
*
|
||||
* Default configuration is 4096, yielding a 5% to 10% duty cycle, or 1ms - 2ms pulse, which is the typical standard
|
||||
* Adjusting the max offset can help account for differences in servos
|
||||
* that means 10% duty cycle does not exactly reach 180 degrees
|
||||
*
|
||||
* This only applies when using SetAngle. SetDirect has no limit on what you can set
|
||||
*/
|
||||
void YEEP144_Servo_SetMaxTicks(uint8_t servo, uint16_t max){
|
||||
if(servo == 0 || servo >= 4) return;
|
||||
servoMaxTicks[servo-1] = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum angle range for a given servo
|
||||
*
|
||||
* Default is 0, for 0-180deg servos
|
||||
*
|
||||
* This only applies to SetAngle
|
||||
*/
|
||||
void YEEP144_Servo_SetMinAngle(uint8_t servo, float min){
|
||||
if(servo == 0 || servo >= 4) return;
|
||||
servoMinDegrees[servo-1] = min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum angle range for a given servo
|
||||
*
|
||||
* Default is 180, for 0-180deg servos
|
||||
*
|
||||
* This only applies to SetAngle
|
||||
*/
|
||||
void YEEP144_Servo_SetMaxAngle(uint8_t servo, float max){
|
||||
if(servo == 0 || servo >= 4) return;
|
||||
servoMaxDegrees[servo-1] = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly set the pulse value of the timer to control the position of the servo
|
||||
* Typically this should be in the range 0-2047
|
||||
* The function will accept larger pulse values, which will result in a duty cycle >10%, or a pulse of >2ms
|
||||
* This may be useful for servos who's maximum angle is not achieved at 10%
|
||||
* Along with setting the zero offset, this should account for troublesome servos, albeit with less elegant maths
|
||||
*/
|
||||
void YEEP144_Servo1_SetDirect(uint16_t pulse){
|
||||
TIM5->CCR1 = pulse + servoMinTicks[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly set the pulse value of the timer to control the position of the servo
|
||||
* Typically this should be in the range 0-2047
|
||||
* The function will accept larger pulse values, which will result in a duty cycle >10%, or a pulse of >2ms
|
||||
* This may be useful for servos who's maximum angle is not achieved at 10%
|
||||
* Along with setting the zero offset, this should account for troublesome servos, albeit with less elegant maths
|
||||
*/
|
||||
void YEEP144_Servo2_SetDirect(uint16_t pulse){
|
||||
TIM5->CCR4 = pulse + servoMinTicks[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly set the pulse value of the timer to control the position of the servo
|
||||
* Typically this should be in the range 0-2047
|
||||
* The function will accept larger pulse values, which will result in a duty cycle >10%, or a pulse of >2ms
|
||||
* This may be useful for servos who's maximum angle is not achieved at 10%
|
||||
* Along with setting the zero offset, this should account for troublesome servos, albeit with less elegant maths
|
||||
*/
|
||||
void YEEP144_Servo3_SetDirect(uint16_t pulse){
|
||||
TIM13->CCR1 = pulse + servoMinTicks[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly set the pulse value of the timer to control the position of the servo
|
||||
* Typically this should be in the range 0-2047
|
||||
* The function will accept larger pulse values, which will result in a duty cycle >10%, or a pulse of >2ms
|
||||
* This may be useful for servos who's maximum angle is not achieved at 10%
|
||||
* Along with setting the zero offset, this should account for troublesome servos, albeit with less elegant maths
|
||||
*/
|
||||
void YEEP144_Servo4_SetDirect(uint16_t pulse){
|
||||
TIM17->CCR1 = pulse + servoMinTicks[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the servo to a specified angle. This will be based on the minimum and maximum angles set for the servo
|
||||
* By default this will work in the range 0 to 180
|
||||
*/
|
||||
void YEEP144_Servo1_SetAngle(float angle){
|
||||
if(angle < servoMinDegrees[0] || angle > servoMaxDegrees[0]) return; // If the angle is out of range, do nothing
|
||||
TIM5->CCR1 = YEEP144_Servo_MapAngleToTicks(angle, servoMinDegrees[0], servoMaxDegrees[0], servoMinTicks[0], servoMaxTicks[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the servo to a specified angle. This will be based on the minimum and maximum angles set for the servo
|
||||
* By default this will work in the range 0 to 180
|
||||
*/
|
||||
void YEEP144_Servo2_SetAngle(float angle){
|
||||
if(angle < servoMinDegrees[1] || angle > servoMaxDegrees[1]) return; // If the angle is out of range, do nothing
|
||||
TIM5->CCR1 = YEEP144_Servo_MapAngleToTicks(angle, servoMinDegrees[1], servoMaxDegrees[1], servoMinTicks[1], servoMaxTicks[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the servo to a specified angle. This will be based on the minimum and maximum angles set for the servo
|
||||
* By default this will work in the range 0 to 180
|
||||
*/
|
||||
void YEEP144_Servo3_SetAngle(float angle){
|
||||
if(angle < servoMinDegrees[0] || angle > servoMaxDegrees[0]) return; // If the angle is out of range, do nothing
|
||||
TIM5->CCR1 = YEEP144_Servo_MapAngleToTicks(angle, servoMinDegrees[0], servoMaxDegrees[0], servoMinTicks[0], servoMaxTicks[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the servo to a specified angle. This will be based on the minimum and maximum angles set for the servo
|
||||
* By default this will work in the range 0 to 180
|
||||
*/
|
||||
void YEEP144_Servo4_SetAngle(float angle){
|
||||
if(angle < servoMinDegrees[0] || angle > servoMaxDegrees[0]) return; // If the angle is out of range, do nothing
|
||||
TIM5->CCR1 = YEEP144_Servo_MapAngleToTicks(angle, servoMinDegrees[0], servoMaxDegrees[0], servoMinTicks[0], servoMaxTicks[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function used to map a float angle to a timer tick value, based on the min and max ranges specified
|
||||
* e.g. map the angle 90 from the range 0 to 180, into the range 2048 - 4095
|
||||
*/
|
||||
uint16_t YEEP144_Servo_MapAngleToTicks(float angle, float minAngle, float maxAngle, uint16_t minTicks, uint16_t maxTicks){
|
||||
// Map the angle value into the tick range using the min and max parameters
|
||||
return (uint16_t)(( ((angle - minAngle) * (maxTicks - minTicks)) / (maxAngle - minAngle) ) + minTicks);
|
||||
}
|
||||
143
YEEP144lib/src/YEEP144_TSOP4838.c
Normal file
143
YEEP144lib/src/YEEP144_TSOP4838.c
Normal file
@@ -0,0 +1,143 @@
|
||||
#include "YEEP144.h"
|
||||
#include "YEEP144_Pinout.h"
|
||||
#include "YEEP144_DebugUART.h"
|
||||
|
||||
#define ASCII_ESC 27
|
||||
|
||||
// NEC Protocol Timing Constants (in microseconds)
|
||||
#define NEC_START_LOW 9000 // 9ms leading pulse
|
||||
#define NEC_START_HIGH 4500 // 4.5ms space
|
||||
#define NEC_BIT_LOW 560 // 560us burst
|
||||
#define NEC_BIT_ONE_HIGH 1690 // 1.69ms space for logical 1
|
||||
#define NEC_BIT_ZERO_HIGH 560 // 560us space for logical 0
|
||||
#define NEC_TOLERANCE 400 // +/- margin of error
|
||||
|
||||
// State Machine States
|
||||
typedef enum{
|
||||
IR_STATE_IDLE,
|
||||
IR_STATE_LEADER,
|
||||
IR_STATE_DATA
|
||||
} IR_State_t;
|
||||
|
||||
// Variables
|
||||
volatile IR_State_t ir_state = IR_STATE_IDLE;
|
||||
volatile uint32_t ir_last_time = 0;
|
||||
volatile uint32_t ir_raw_bits = 0;
|
||||
volatile uint8_t ir_bit_count = 0;
|
||||
volatile uint8_t ir_code_ready = 0;
|
||||
volatile uint32_t ir_code = 0;
|
||||
|
||||
// Enable hardware DWT (Data Watchpoint and Trace) cycle counter
|
||||
void DWT_Init(void) {
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CYCCNT = 0;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
}
|
||||
|
||||
// Arduino-style microsecond measurement
|
||||
uint32_t micros(void) {
|
||||
return DWT->CYCCNT / (SystemCoreClock / 1000000);
|
||||
}
|
||||
|
||||
// This exact function name matches the STM32H563 hardware vector table entry
|
||||
void EXTI3_IRQHandler(void)
|
||||
{
|
||||
// Tells the HAL layer to clear the flag and fire your callback routine
|
||||
HAL_GPIO_EXTI_IRQHandler(IR_RECEIVER_PIN);
|
||||
}
|
||||
|
||||
void YEEP144_TSOP4838_Init(){
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
|
||||
// Enable DWT for microsecond timing
|
||||
DWT_Init();
|
||||
|
||||
// Configure Pin F3 for Interrupt on Both Edges
|
||||
GPIO_InitStruct.Pin = IR_RECEIVER_PIN;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; // Triggers on rising or falling change
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL; // Uses the TSOP4838 internal pull-up
|
||||
HAL_GPIO_Init(IR_RECEIVER_PORT, &GPIO_InitStruct);
|
||||
|
||||
// Enable and set EXTI Line 3 Vector for STM32H563
|
||||
HAL_NVIC_SetPriority(IR_RECEIVER_EXTI, 2, 0);
|
||||
HAL_NVIC_EnableIRQ(IR_RECEIVER_EXTI);
|
||||
}
|
||||
|
||||
uint8_t YEEP144_TSOP4838_Received(void){
|
||||
return ir_code_ready;
|
||||
}
|
||||
|
||||
uint32_t YEEP144_TSOP4838_Read(void){
|
||||
return ir_code;
|
||||
ir_code_ready = 0;
|
||||
}
|
||||
|
||||
// These functions override the weak HAL geometric EXTI callbacks
|
||||
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin){
|
||||
if (GPIO_Pin == IR_RECEIVER_PIN)
|
||||
{
|
||||
uint32_t current_time = micros();
|
||||
uint32_t duration = current_time - ir_last_time;
|
||||
ir_last_time = current_time;
|
||||
switch(ir_state) {
|
||||
case IR_STATE_LEADER: // Rising edge (End of 9ms pulse)
|
||||
if (duration < (NEC_START_LOW - NEC_TOLERANCE) || duration > (NEC_START_LOW + NEC_TOLERANCE)){
|
||||
ir_state = IR_STATE_IDLE; // False start
|
||||
}
|
||||
break;
|
||||
case IR_STATE_IDLE:
|
||||
break;
|
||||
case IR_STATE_DATA:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For newer STM32 HAL libraries, rising and falling callbacks are separated for memory efficiency
|
||||
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin){
|
||||
if (GPIO_Pin == IR_RECEIVER_PIN)
|
||||
{
|
||||
uint32_t current_time = micros();
|
||||
uint32_t duration = current_time - ir_last_time;
|
||||
ir_last_time = current_time;
|
||||
switch(ir_state){
|
||||
case IR_STATE_IDLE: // Falling edge (Start of 9ms pulse)
|
||||
ir_state = IR_STATE_LEADER;
|
||||
break;
|
||||
case IR_STATE_LEADER: // Falling edge (End of 4.5ms space)
|
||||
if (duration >= (NEC_START_HIGH - NEC_TOLERANCE) && duration <= (NEC_START_HIGH + NEC_TOLERANCE)){
|
||||
ir_state = IR_STATE_DATA;
|
||||
ir_bit_count = 0;
|
||||
ir_raw_bits = 0;
|
||||
} else {
|
||||
ir_state = IR_STATE_IDLE; // Invalid preamble
|
||||
}
|
||||
break;
|
||||
case IR_STATE_DATA: // Falling edge (End of a data bit's high space)
|
||||
// The high duration determines if the bit is a 0 or a 1
|
||||
if (duration >= (NEC_BIT_ZERO_HIGH - NEC_TOLERANCE) && duration <= (NEC_BIT_ZERO_HIGH + NEC_TOLERANCE)) {
|
||||
ir_raw_bits >>= 1; // It's a 0
|
||||
ir_bit_count++;
|
||||
}
|
||||
else if (duration >= (NEC_BIT_ONE_HIGH - NEC_TOLERANCE) && duration <= (NEC_BIT_ONE_HIGH + NEC_TOLERANCE)) {
|
||||
ir_raw_bits >>= 1;
|
||||
ir_raw_bits |= 0x80000000; // It's a 1
|
||||
ir_bit_count++;
|
||||
}
|
||||
else {
|
||||
ir_state = IR_STATE_IDLE; // Timing error, reset state
|
||||
}
|
||||
|
||||
uint32_t ir_bits = 0;
|
||||
if (ir_bit_count == 32) {
|
||||
ir_bits = ir_raw_bits;
|
||||
ir_code = (ir_bits >> 16) & 0xFF;
|
||||
uint32_t command_inverse = (ir_bits >> 24) & 0xFF;
|
||||
if (ir_code == (uint8_t)(~command_inverse))
|
||||
ir_code_ready = 1;
|
||||
ir_state = IR_STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user