Skip to content

Instantly share code, notes, and snippets.

@albertzak
Last active June 28, 2016 16:59
Show Gist options
  • Save albertzak/8d826645f0ea5dec387f88bc77c2939e to your computer and use it in GitHub Desktop.
Save albertzak/8d826645f0ea5dec387f88bc77c2939e to your computer and use it in GitHub Desktop.
blink morse code
#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