Last active
June 28, 2016 16:59
-
-
Save albertzak/8d826645f0ea5dec387f88bc77c2939e to your computer and use it in GitHub Desktop.
blink morse code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "stdlib.h" | |
#include "stm32f4xx.h" | |
// This morse code implementation uses the standard as defined | |
// by the International Telecommunication Union (ITU) in 1865 | |
// T is short for "Time Unit" | |
int T = (100 * 100000); | |
// Morse code is composed of five elements: | |
// Two elements and three gaps | |
// The DOT element is one time unit long | |
#define DOT (1 * T) | |
// The DASH element is three time units long | |
#define DASH (3 * T) | |
// Morse code defines different gaps between | |
// elements, letters and words | |
// The gap between elements is one time unit long | |
#define GAP (1 * T) | |
// The gap between letters are 3 time units long | |
#define GAP_LETTER (3 * T) | |
// The gap between words are 7 time units long | |
#define GAP_WORD (7 * T) | |
// Now on to the actual alphabet | |
char * glyphs[] = { | |
// Numbers | |
// Fun fact: | |
// The line numbers correspond to the | |
// decimal ASCII code of the glyphs. | |
// | |
// | |
// Total coincidence. | |
// | |
// I swear. | |
"-----", // 0 | |
".----", // 1 | |
"..---", // 2 | |
"...--", // 3 | |
"....-", // 4 | |
".....", // 5 | |
"-....", // 6 | |
"--...", // 7 | |
"---..", // 8 | |
"----.", // 9 | |
// Haha, see? If the line numbers thing didn't work out: | |
// the '0' above should have been at line 48 | |
// and the 'A' below should be at line 65 | |
// Letters | |
".-", // A | |
"-...", // B | |
"-.-.", // C | |
"-..", // D | |
".", // E | |
"..-.", // F | |
"--.", // G | |
"....", // H | |
"..", // I | |
".---", // J | |
".-.-", // K | |
".-..", // L | |
"--", // M | |
"-.", // N | |
"---", // O | |
".--.", // P | |
"--.-", // Q | |
".-.", // R | |
"...", // S | |
"-", // T | |
"..-", // U | |
"...-", // V | |
".--", // W | |
"-..-", // X | |
"-.--", // Y | |
"--..", // Z | |
}; | |
// Pass in a character and get back a | |
// pointer to the morse code string in | |
// the above glyphs array | |
char * get_morse_code(char character) { | |
// Check if the character is a number or a letter, | |
// and subtract the ASCII offset to calculate the | |
// index of the corresponding morse code | |
// in our glyphs array | |
if (('0' <= character) && (character <= '9')) { | |
// It's a number! | |
return glyphs[character - '0']; | |
} else if (('A' <= character) && (character <= 'Z')) { | |
// It's an uppercase letter! | |
// The +10 offsets the numbers that occupy | |
// glyph array indices 0 to 9 | |
return glyphs[character - 'A' + 10]; | |
} else if (('a' <= character) && (character <= 'z')) { | |
// It's a lowercase letter! | |
return glyphs[character - 'a' + 10]; | |
} else { | |
// It's not defined in ITU morse code... | |
return NULL; | |
} | |
} | |
// A general purpose busy waiting loop | |
void sleep(int ms) { | |
for(int i = 0; i < ms; i++); | |
} | |
#define ON() GPIO_SetBits(GPIOA, GPIO_Pin_5); | |
#define OFF() GPIO_ResetBits(GPIOA, GPIO_Pin_5); | |
void dot() { ON(); sleep(DOT); OFF(); } | |
void dash() { ON(); sleep(DASH); OFF(); } | |
void gap() { sleep(GAP); } | |
void gap_letter() { sleep(GAP_LETTER); } | |
void gap_word() { sleep(GAP_WORD); } | |
// This function takes a single '.' or '-' | |
// and blinks a dot or dash | |
void blink_morse_element(char character) { | |
if (character == '.') { dot(); } | |
else if (character == '-') { dash(); } | |
} | |
void blink_morse(char character) { | |
char * code; | |
code = get_morse_code(character); | |
// Return if there's no ITU morse code for this character | |
if (! code) { dash(); return; } | |
// The code string looks like this: | |
// ".-." (This would be an 'R') | |
// We'll move the character pointer one char at | |
// a time until we hit the NULL terminator | |
while (*code) { | |
blink_morse_element(*code); | |
gap(); | |
code++; | |
} | |
} | |
// This handler gets called when the receive buffer | |
// of USART2 is not empty, that is if there was data received. | |
void USART2_IRQHandler() { | |
USART_ClearITPendingBit(USART2, USART_IT_RXNE); | |
// Now we'll read the received byte and cast it to a character | |
char character; | |
character = (char) USART_ReceiveData(USART2); | |
blink_morse(character); | |
} | |
// This fires about once a second to update the | |
// morse time base by reading the ADC value | |
void TIM4_IRQHandler() { | |
TIM_ClearITPendingBit(TIM4, TIM_IT_Update); | |
ADC_SoftwareStartConv(ADC1); | |
sleep(50); | |
int speed = ADC_GetConversionValue(ADC1); | |
T = 2000000 + speed * 100000; | |
} | |
int main() { | |
// Enable clock for GPIOA, USART2, TIm4, and ADC1 via RCC | |
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); | |
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); | |
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); | |
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); | |
// Initialize GPIO A with USART alternate function (AF) | |
// We only need to receive data, so we only need one pin | |
// The alternate function of Pin 3 of GPIO A is USART2_RX | |
GPIO_InitTypeDef GPIOInit; | |
GPIOInit.GPIO_Mode = GPIO_Mode_AF; | |
GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL; | |
GPIOInit.GPIO_Pin = GPIO_Pin_3; | |
GPIO_Init(GPIOA, &GPIOInit); | |
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); | |
// We'll also configure GPIO A Pin 5, which is the built in LED | |
GPIOInit.GPIO_Pin = GPIO_Pin_5; | |
GPIOInit.GPIO_Mode = GPIO_Mode_OUT; | |
GPIO_Init(GPIOA, &GPIOInit); | |
// And finally let's configure an analog input for the potentiometer | |
GPIOInit.GPIO_Mode = GPIO_Mode_AN; | |
GPIOInit.GPIO_Pin = GPIO_Pin_0; | |
GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL; | |
GPIOInit.GPIO_Speed = GPIO_Speed_100MHz; | |
GPIO_Init(GPIOA, &GPIOInit); | |
// Hello world! | |
dot(); | |
// Let's configure the ADC | |
// First, the common ADC Config | |
ADC_CommonInitTypeDef ADCCommonInitStruct; | |
ADCCommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; | |
ADCCommonInitStruct.ADC_Mode = ADC_Mode_Independent; | |
ADCCommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4; | |
ADCCommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles; | |
ADC_CommonInit(&ADCCommonInitStruct); | |
// Convert continuously | |
ADC_InitTypeDef ADCInitStruct; | |
ADCInitStruct.ADC_ContinuousConvMode = DISABLE; | |
ADCInitStruct.ADC_DataAlign = ADC_DataAlign_Right; | |
ADCInitStruct.ADC_NbrOfConversion = 1; | |
ADCInitStruct.ADC_Resolution = ADC_Resolution_8b; | |
ADC_Init(ADC1, &ADCInitStruct); | |
// Config regular ADC group 0 | |
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_480Cycles); | |
// Start ADC | |
ADC_Cmd(ADC1, ENABLE); | |
// And kick off first conversion | |
ADC_SoftwareStartConv(ADC1); | |
// Now, on to the USART | |
// Initialize USART with standard settings as receive-only | |
USART_InitTypeDef USARTInit; | |
USARTInit.USART_BaudRate = 9600; | |
USARTInit.USART_Mode = USART_Mode_Rx; | |
USARTInit.USART_WordLength = USART_WordLength_8b; | |
USARTInit.USART_Parity = USART_Parity_No; | |
USARTInit.USART_HardwareFlowControl = USART_HardwareFlowControl_None; | |
USARTInit.USART_StopBits = USART_StopBits_1; | |
// Start USART | |
USART_Init(USART2, &USARTInit); | |
USART_Cmd(USART2, ENABLE); | |
// Configure an interrupt for receiving data | |
// This fires when the receive buffer is not empty, | |
// which means we received a byte that we're ready to read | |
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); | |
// Configure interrupt controller | |
// This interrupt for the USART has a lower | |
// priority than the potentiometer to make it | |
// possible to change the morse speed during send | |
NVIC_InitTypeDef NVICInit; | |
NVICInit.NVIC_IRQChannel = USART2_IRQn; | |
NVICInit.NVIC_IRQChannelCmd = ENABLE; | |
NVICInit.NVIC_IRQChannelPreemptionPriority = 1; | |
NVICInit.NVIC_IRQChannelSubPriority = 0; | |
NVIC_Init(&NVICInit); | |
// Let's also configure a timer that fires an interrupt | |
// to read the value of the potentiometer that is connected | |
// to the ADC | |
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; | |
// Prescaler: 50MHz -> 1 kHz | |
TIM_TimeBaseInitStructure.TIM_Prescaler = 49999; | |
// Period: 1kHz -> 10 Hz | |
TIM_TimeBaseInitStructure.TIM_Period = 100; | |
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure); | |
TIM_Cmd(TIM4, ENABLE); | |
// Configure TIM4 to send interrupt | |
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); | |
// And allow the interrupt through NVIC, | |
// albeit with a lower priority than the USART | |
NVICInit.NVIC_IRQChannel = TIM4_IRQn; | |
NVICInit.NVIC_IRQChannelPreemptionPriority = 1; | |
NVIC_Init(&NVICInit); | |
// Good night kernel. | |
while(1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment