mirror of
https://github.com/eddyem/mmpp.git
synced 2026-03-22 01:31:17 +03:00
start of STM32 programming (very-very pre-alpha version)
This commit is contained in:
137
STM32/steppers/Makefile
Normal file
137
STM32/steppers/Makefile
Normal file
@@ -0,0 +1,137 @@
|
||||
BINARY = steppers
|
||||
BOOTPORT ?= /dev/ttyUSB0
|
||||
BOOTSPEED ?= 115200
|
||||
# MCU FAMILY
|
||||
FAMILY = F0
|
||||
# MCU code
|
||||
MCU = F030x4
|
||||
DEFS = -DEBUG
|
||||
# change this linking script depending on particular MCU model,
|
||||
# for example, if you have STM32F103VBT6, you should write:
|
||||
LDSCRIPT = ld/stm32f030f.ld
|
||||
|
||||
INDEPENDENT_HEADERS=
|
||||
|
||||
FP_FLAGS ?= -msoft-float
|
||||
ASM_FLAGS = -mthumb -mcpu=cortex-m0 -march=armv6-m -mtune=cortex-m0
|
||||
ARCH_FLAGS = $(ASM_FLAGS) $(FP_FLAGS)
|
||||
|
||||
###############################################################################
|
||||
# Executables
|
||||
PREFIX ?= /opt/bin/arm-none-eabi
|
||||
|
||||
RM := rm -f
|
||||
RMDIR := rmdir
|
||||
CC := $(PREFIX)-gcc
|
||||
LD := $(PREFIX)-gcc
|
||||
AR := $(PREFIX)-ar
|
||||
AS := $(PREFIX)-as
|
||||
OBJCOPY := $(PREFIX)-objcopy
|
||||
OBJDUMP := $(PREFIX)-objdump
|
||||
GDB := $(PREFIX)-gdb
|
||||
STFLASH := $(shell which st-flash)
|
||||
STBOOT := $(shell which stm32flash)
|
||||
|
||||
###############################################################################
|
||||
# Source files
|
||||
OBJDIR = mk
|
||||
LDSCRIPT ?= $(BINARY).ld
|
||||
SRC := $(wildcard *.c)
|
||||
OBJS := $(addprefix $(OBJDIR)/, $(SRC:%.c=%.o))
|
||||
STARTUP = $(OBJDIR)/startup.o
|
||||
OBJS += $(STARTUP)
|
||||
DEPS := $(OBJS:.o=.d)
|
||||
|
||||
INC_DIR ?= ../inc
|
||||
|
||||
INCLUDE := -I$(INC_DIR)/F0 -I$(INC_DIR)/cm
|
||||
LIB_DIR := $(INC_DIR)/ld
|
||||
|
||||
###############################################################################
|
||||
# C flags
|
||||
CFLAGS += -O2 -g -MD -D__thumb2__=1
|
||||
CFLAGS += -Wall -Werror -Wextra -Wshadow -Wimplicit-function-declaration
|
||||
CFLAGS += -Wredundant-decls $(INCLUDE)
|
||||
# -Wmissing-prototypes -Wstrict-prototypes
|
||||
CFLAGS += -fno-common -ffunction-sections -fdata-sections
|
||||
|
||||
###############################################################################
|
||||
# Linker flags
|
||||
LDFLAGS += --static -nostartfiles
|
||||
#--specs=nano.specs
|
||||
LDFLAGS += -L$(LIB_DIR)
|
||||
LDFLAGS += -T$(LDSCRIPT)
|
||||
LDFLAGS += -Wl,-Map=$(OBJDIR)/$(BINARY).map
|
||||
LDFLAGS += -Wl,--gc-sections
|
||||
|
||||
###############################################################################
|
||||
# Used libraries
|
||||
LDLIBS += -Wl,--start-group -lc -lgcc -Wl,--end-group
|
||||
LDLIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
|
||||
|
||||
DEFS += -DSTM32$(FAMILY) -DSTM32$(MCU)
|
||||
|
||||
#.SUFFIXES: .elf .bin .hex .srec .list .map .images
|
||||
#.SECONDEXPANSION:
|
||||
#.SECONDARY:
|
||||
|
||||
ELF := $(OBJDIR)/$(BINARY).elf
|
||||
LIST := $(OBJDIR)/$(BINARY).list
|
||||
BIN := $(BINARY).bin
|
||||
HEX := $(BINARY).hex
|
||||
|
||||
all: bin list
|
||||
|
||||
elf: $(ELF)
|
||||
bin: $(BIN)
|
||||
hex: $(HEX)
|
||||
list: $(LIST)
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir $(OBJDIR)
|
||||
|
||||
$(STARTUP): $(INC_DIR)/startup/vector.c
|
||||
$(CC) $(CFLAGS) $(DEFS) $(INCLUDE) $(ARCH_FLAGS) -o $@ -c $<
|
||||
|
||||
$(OBJDIR)/%.o: %.c
|
||||
@echo " CC $<"
|
||||
$(CC) $(CFLAGS) $(DEFS) $(INCLUDE) $(ARCH_FLAGS) -o $@ -c $<
|
||||
|
||||
#$(OBJDIR)/%.d: %.c $(OBJDIR)
|
||||
# $(CC) -MM -MG $< | sed -e 's,^\([^:]*\)\.o[ ]*:,$(@D)/\1.o $(@D)/\1.d:,' >$@
|
||||
|
||||
$(BIN): $(ELF)
|
||||
@echo " OBJCOPY $(BIN)"
|
||||
$(OBJCOPY) -Obinary $(ELF) $(BIN)
|
||||
|
||||
$(HEX): $(ELF)
|
||||
@echo " OBJCOPY $(HEX)"
|
||||
$(OBJCOPY) -Oihex $(ELF) $(HEX)
|
||||
|
||||
$(LIST): $(ELF)
|
||||
@echo " OBJDUMP $(LIST)"
|
||||
$(OBJDUMP) -S $(ELF) > $(LIST)
|
||||
|
||||
$(ELF): $(OBJDIR) $(OBJS)
|
||||
@echo " LD $(ELF)"
|
||||
$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(ELF)
|
||||
|
||||
clean:
|
||||
@echo " CLEAN"
|
||||
$(RM) $(OBJS) $(DEPS) $(ELF) $(HEX) $(LIST) $(OBJDIR)/*.map
|
||||
@rmdir $(OBJDIR) 2>/dev/null || true
|
||||
|
||||
|
||||
flash: $(BIN)
|
||||
@echo " FLASH $(BIN)"
|
||||
$(STFLASH) write $(BIN) 0x8000000
|
||||
|
||||
boot: $(BIN)
|
||||
@echo " LOAD $(BIN) through bootloader"
|
||||
$(STBOOT) -b$(BOOTSPEED) $(BOOTPORT) -w $(BIN)
|
||||
|
||||
.PHONY: clean flash boot
|
||||
26
STM32/steppers/Readme.md
Normal file
26
STM32/steppers/Readme.md
Normal file
@@ -0,0 +1,26 @@
|
||||
Management of two stepper motors
|
||||
================================
|
||||
|
||||
Based on STM32F030F4P6
|
||||
|
||||
## Pinout
|
||||
|
||||
|Pin | Type | Role |
|
||||
|:---|:----:|:------------------------|
|
||||
|PA0 | AIN | Steppers current |
|
||||
|PA1 | AIN | Input voltage 12V |
|
||||
|PA2 | AIN | EndSwitch2 of motor1 |
|
||||
|PA3 | AIN | EndSwitch1 of motor1 |
|
||||
|PA4 | PUPD | Tim14Ch1 - motor1 steps |
|
||||
|PA5 | PUPD | Motor2 enable |
|
||||
|PA6 | PUPD | Tim3Ch1 - motor2 steps |
|
||||
|PA7 | PUPD | Motor2 direction |
|
||||
|PA9 | OD | USART1 Tx |
|
||||
|PA10| FIN | USART1 Rx |
|
||||
|PA13| AIN | EndSwitch1 of motor2 |
|
||||
|PA14| AIN | EndSwitch2 of motor2 |
|
||||
|PB1 | PUPD | Turn off motors' power |
|
||||
|PF0 | PUPD | Motor1 enable |
|
||||
|PF1 | PUPD | Motor1 direction |
|
||||
|
||||
## Protocol
|
||||
149
STM32/steppers/adc.c
Normal file
149
STM32/steppers/adc.c
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* adc.c
|
||||
*
|
||||
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
#include "stm32f0.h"
|
||||
#include "flash.h"
|
||||
#include "adc.h"
|
||||
|
||||
/*
|
||||
* 0 - Steppers current
|
||||
* 1 - Input voltage 12V
|
||||
* 2 - EndSwitch2 of motor1
|
||||
* 3 - EndSwitch1 of motor1
|
||||
* 4 - EndSwitch1 of motor2
|
||||
* 5 - EndSwitch2 of motor2
|
||||
* 6 - inner temperature
|
||||
* 7 - vref
|
||||
*/
|
||||
uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS];
|
||||
|
||||
void adc_setup(){
|
||||
// AIN: PA0..3, PA13, PA14. ADC_IN16 - inner temperature. ADC_IN17 - VREFINT
|
||||
/* (1) Enable the peripheral clock of the ADC */
|
||||
/* (2) Set peripheral prescaler to /2 so PCLK = HCLK/2 = 24MHz */
|
||||
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; /* (1) */
|
||||
RCC->CFGR |= RCC_CFGR_PPRE_2; /* (2) */
|
||||
/* (1) Ensure that ADEN = 0 */
|
||||
/* (2) Clear ADEN */
|
||||
/* (3) Launch the calibration by setting ADCAL */
|
||||
/* (4) Wait until ADCAL=0 */
|
||||
if ((ADC1->CR & ADC_CR_ADEN) != 0){ /* (1) */
|
||||
ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); /* (2) */
|
||||
}
|
||||
ADC1->CR |= ADC_CR_ADCAL; /* (3) */
|
||||
while ((ADC1->CR & ADC_CR_ADCAL) != 0){} /* (4) */
|
||||
/* (1) Enable the ADC */
|
||||
/* (2) Wait until ADC ready */
|
||||
do{
|
||||
ADC1->CR |= ADC_CR_ADEN; /* (1) */
|
||||
}while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) /* (2) */;
|
||||
/* (1) Select PCLK/2 by writing 01 in CKMODE */
|
||||
/* (2) Select the continuous mode */
|
||||
/* (3) Select CHSEL0..3, 13,14, 16,17 */
|
||||
/* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater than 17.1us */
|
||||
/* (5) Wake-up the VREFINT and Temperature sensor (only for VBAT, Temp sensor and VRefInt) */
|
||||
ADC1->CFGR2 |= ADC_CFGR2_CKMODE_0; /* (1) */
|
||||
ADC1->CFGR1 |= ADC_CFGR1_CONT; /* (2)*/
|
||||
ADC1->CHSELR = ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL1 | ADC_CHSELR_CHSEL2 |
|
||||
ADC_CHSELR_CHSEL3 | ADC_CHSELR_CHSEL13 | ADC_CHSELR_CHSEL14 |
|
||||
ADC_CHSELR_CHSEL16 | ADC_CHSELR_CHSEL17; /* (3)*/
|
||||
ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; /* (4) */
|
||||
ADC->CCR |= ADC_CCR_TSEN | ADC_CCR_VREFEN; /* (5) */
|
||||
// DMA for AIN
|
||||
/* (1) Enable the peripheral clock on DMA */
|
||||
/* (2) Enable DMA transfer on ADC and circular mode */
|
||||
/* (3) Configure the peripheral data register address */
|
||||
/* (4) Configure the memory address */
|
||||
/* (5) Configure the number of DMA tranfer to be performs on DMA channel 1 */
|
||||
/* (6) Configure increment, size, interrupts and circular mode */
|
||||
/* (7) Enable DMA Channel 1 */
|
||||
RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* (1) */
|
||||
ADC1->CFGR1 |= ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG; /* (2) */
|
||||
DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); /* (3) */
|
||||
DMA1_Channel1->CMAR = (uint32_t)(ADC_array); /* (4) */
|
||||
DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS; /* (5) */
|
||||
DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC; /* (6) */
|
||||
DMA1_Channel1->CCR |= DMA_CCR_EN; /* (7) */
|
||||
ADC1->CR |= ADC_CR_ADSTART; /* start the ADC conversions */
|
||||
}
|
||||
|
||||
// return MCU temperature (degrees of celsius)
|
||||
uint32_t getTemp(){
|
||||
uint32_t temperature = ADC_array[6];
|
||||
temperature = ((temperature * VDD_APPLI / VDD_CALIB) - (uint32_t) *TEMP30_CAL_ADDR ) ;
|
||||
temperature *= (uint32_t)(110 - 30);
|
||||
temperature /= (uint32_t)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR);
|
||||
temperature += 30;
|
||||
return(temperature);
|
||||
}
|
||||
|
||||
//static uint32_t calval = 0;
|
||||
// return Vdd * 10 (V)
|
||||
uint32_t getVdd(){
|
||||
/* if(!calval){
|
||||
calval = ((uint32_t) *VREFINT_CAL_ADDR) * VDD_CALIB;
|
||||
calval /= VDD_APPLI;
|
||||
} */
|
||||
uint32_t vdd = ADC_array[7] * (uint32_t)33 * the_conf.v33numerator; // 3.3V
|
||||
//vdd /= calval * the_conf.v33denominator;
|
||||
vdd /= ((uint32_t) *VREFINT_CAL_ADDR) * the_conf.v33denominator;
|
||||
return vdd;
|
||||
}
|
||||
|
||||
// return value of 12V * 10 (V)
|
||||
uint32_t getVmot(){
|
||||
uint32_t vmot = ADC_array[1] * getVdd() * the_conf.v12numerator;
|
||||
vmot >>= 12;
|
||||
vmot /= the_conf.v12denominator;
|
||||
return vmot;
|
||||
}
|
||||
|
||||
// return value of motors' current * 100 (A)
|
||||
uint32_t getImot(){
|
||||
uint32_t vmot = ADC_array[0] * getVdd() * the_conf.i12numerator * 10;
|
||||
vmot >>= 12;
|
||||
vmot /= the_conf.i12denominator;
|
||||
return vmot;
|
||||
}
|
||||
|
||||
// end-switches status: 0 - don't activated, 1 - activated, 2 - user button, 4 - error
|
||||
// @param motnum - motor number (0,1)
|
||||
// @param eswnum - switch number (0,1)
|
||||
ESW_status eswStatus(int motnum, int eswnum){
|
||||
int idx;
|
||||
if(motnum){ // motor 1
|
||||
if(eswnum) idx = 5;
|
||||
else idx = 4;
|
||||
}else{ // motor 0
|
||||
if(eswnum) idx = 3;
|
||||
else idx = 2;
|
||||
}
|
||||
uint16_t thres = the_conf.ESW_thres, val = ADC_array[idx];
|
||||
// low sighal: 0..threshold - Hall activated
|
||||
if(val < thres) return ESW_HALL;
|
||||
// high signal: (4096-thres)..4096 - pullup
|
||||
if(val > (uint16_t)0x1000 - thres) return ESW_RELEASED;
|
||||
// middle signal: 0x800-thres..0x800+thres - user button active (47k pullup + 47k pulldown)
|
||||
if(0x800 - thres < val && val < 0x800 + thres) return ESW_BUTTON;
|
||||
// very strange, return err
|
||||
return ESW_ERROR;
|
||||
}
|
||||
47
STM32/steppers/adc.h
Normal file
47
STM32/steppers/adc.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* adc.h
|
||||
*
|
||||
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __ADC_H__
|
||||
#define __ADC_H__
|
||||
|
||||
// 8 channels (including inttemp & vrefint)
|
||||
#define NUMBER_OF_ADC_CHANNELS (8)
|
||||
|
||||
extern uint16_t ADC_array[];
|
||||
void adc_setup();
|
||||
|
||||
typedef enum{
|
||||
ESW_RELEASED,
|
||||
ESW_HALL,
|
||||
ESW_BUTTON,
|
||||
ESW_ERROR
|
||||
} ESW_status;
|
||||
|
||||
uint32_t getTemp();
|
||||
uint32_t getVdd();
|
||||
uint32_t getVmot();
|
||||
uint32_t getImot();
|
||||
ESW_status eswStatus(int motnum, int eswnum);
|
||||
|
||||
#endif // __ADC_H__
|
||||
144
STM32/steppers/flash.c
Normal file
144
STM32/steppers/flash.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* flash.c
|
||||
*
|
||||
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
#include "stm32f0.h"
|
||||
#include <string.h> // memcpy
|
||||
|
||||
#include "flash.h"
|
||||
#include "usart.h"
|
||||
|
||||
// start of configuration data in flash (from 15kB, one kB size)
|
||||
#define FLASH_CONF_START_ADDR ((uint32_t)0x08003C00)
|
||||
|
||||
user_conf the_conf = {
|
||||
.good_data_pos = 0xffffffff
|
||||
,.devID = 0
|
||||
,.v12numerator = 1
|
||||
,.v12denominator = 1
|
||||
,.i12numerator = 1
|
||||
,.i12denominator = 1
|
||||
,.v33denominator = 1
|
||||
,.v33numerator = 1
|
||||
,.ESW_thres = 150
|
||||
};
|
||||
|
||||
static int maxnum = 0x800 / sizeof(user_conf);
|
||||
|
||||
static int erase_flash();
|
||||
|
||||
static int get_gooddata(){
|
||||
user_conf *c = (user_conf*) FLASH_CONF_START_ADDR;
|
||||
uint32_t datapos = c->good_data_pos;
|
||||
if(datapos == 0xffffffff){ // virginity clear
|
||||
return maxnum;
|
||||
}
|
||||
// have data - move it to `the_conf`
|
||||
if(maxnum > 32) maxnum = 32;
|
||||
int idx;
|
||||
for(idx = 1; idx < maxnum; ++idx){ // find current settings index - first non-zero bit
|
||||
if(datapos & 1<<idx){
|
||||
break;
|
||||
}
|
||||
}
|
||||
return idx-1;
|
||||
}
|
||||
|
||||
void get_userconf(){
|
||||
user_conf *c = (user_conf*) FLASH_CONF_START_ADDR;
|
||||
int idx = get_gooddata();
|
||||
if(idx == maxnum) return;
|
||||
memcpy(&the_conf, &c[idx], sizeof(user_conf));
|
||||
}
|
||||
|
||||
// store new configuration
|
||||
// @return 0 if all OK
|
||||
int store_userconf(){
|
||||
char buf[2] = {0,0};
|
||||
int ret = 0;
|
||||
user_conf *c = (user_conf*) FLASH_CONF_START_ADDR;
|
||||
int idx = get_gooddata();
|
||||
if(idx == maxnum || idx == maxnum - 1){ // first run or there's no more place
|
||||
idx = 0;
|
||||
if(erase_flash()) return 1;
|
||||
}else ++idx; // take next data position
|
||||
if (FLASH->CR & FLASH_CR_LOCK){
|
||||
FLASH->KEYR = FLASH_FKEY1;
|
||||
FLASH->KEYR = FLASH_FKEY2;
|
||||
}
|
||||
the_conf.good_data_pos = 0xffffffff ^ (1<<idx); // write zero to corresponding position
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
uint16_t *data = (uint16_t*) &the_conf;
|
||||
uint16_t *address = (uint16_t*) &c[idx];
|
||||
uint32_t i, count = sizeof(user_conf) / 2;
|
||||
for (i = 0; i < count; ++i){
|
||||
//*(volatile uint16_t*)(address + i) = (((uint8_t)data[i + 1]) << 8) | data[i];
|
||||
*(volatile uint16_t*)(address + i) = data[i];
|
||||
//while (!(FLASH->SR & FLASH_SR_EOP));
|
||||
while((FLASH->SR & FLASH_SR_BSY));
|
||||
buf[0] = '0' + i;
|
||||
usart1_send_blocking(buf);
|
||||
if(FLASH->SR & FLASH_SR_PGERR) ret = 1;
|
||||
buf[0] = ret + '0';
|
||||
usart1_send_blocking(buf);
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR;
|
||||
}
|
||||
FLASH->CR &= ~(FLASH_CR_PG);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int erase_flash(){
|
||||
int ret = 0;
|
||||
/* (1) Wait till no operation is on going */
|
||||
/* (2) Clear error & EOP bits */
|
||||
/* (3) Check that the Flash is unlocked */
|
||||
/* (4) Perform unlock sequence */
|
||||
while ((FLASH->SR & FLASH_SR_BSY) != 0){} /* (1) */
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR; /* (2) */
|
||||
/* if (FLASH->SR & FLASH_SR_EOP){
|
||||
FLASH->SR |= FLASH_SR_EOP;
|
||||
}*/
|
||||
if ((FLASH->CR & FLASH_CR_LOCK) != 0){ /* (3) */
|
||||
FLASH->KEYR = FLASH_FKEY1; /* (4) */
|
||||
FLASH->KEYR = FLASH_FKEY2;
|
||||
}
|
||||
/* (1) Set the PER bit in the FLASH_CR register to enable page erasing */
|
||||
/* (2) Program the FLASH_AR register to select a page to erase */
|
||||
/* (3) Set the STRT bit in the FLASH_CR register to start the erasing */
|
||||
/* (4) Wait until the EOP flag in the FLASH_SR register set */
|
||||
/* (5) Clear EOP flag by software by writing EOP at 1 */
|
||||
/* (6) Reset the PER Bit to disable the page erase */
|
||||
FLASH->CR |= FLASH_CR_PER; /* (1) */
|
||||
FLASH->AR = FLASH_CONF_START_ADDR; /* (2) */
|
||||
FLASH->CR |= FLASH_CR_STRT; /* (3) */
|
||||
while(!(FLASH->SR & FLASH_SR_EOP));
|
||||
FLASH->SR |= FLASH_SR_EOP; /* (5)*/
|
||||
if(FLASH->SR & FLASH_SR_WRPERR){ /* Check Write protection error */
|
||||
ret = 1;
|
||||
FLASH->SR |= FLASH_SR_WRPERR; /* Clear the flag by software by writing it at 1*/
|
||||
}
|
||||
FLASH->CR &= ~FLASH_CR_PER; /* (6) */
|
||||
return ret;
|
||||
}
|
||||
|
||||
46
STM32/steppers/flash.h
Normal file
46
STM32/steppers/flash.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* flash.h
|
||||
*
|
||||
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __FLASH_H__
|
||||
#define __FLASH_H__
|
||||
|
||||
typedef struct{
|
||||
uint32_t good_data_pos; // position of data (index of mostly left zero)
|
||||
uint16_t devID; // device address (id)
|
||||
uint16_t ESW_thres; // ADC threshold for end-switches/Hall sensors
|
||||
// calibration values for current/voltage sensors
|
||||
uint16_t v12numerator; // 12V to motors
|
||||
uint16_t v12denominator;
|
||||
uint16_t i12numerator; // motors' current
|
||||
uint16_t i12denominator;
|
||||
uint16_t v33numerator; // 3.3V (vref)
|
||||
uint16_t v33denominator;
|
||||
} user_conf;
|
||||
|
||||
extern user_conf the_conf;
|
||||
|
||||
void get_userconf();
|
||||
int store_userconf();
|
||||
|
||||
#endif // __FLASH_H__
|
||||
12
STM32/steppers/ld/stm32f030f.ld
Normal file
12
STM32/steppers/ld/stm32f030f.ld
Normal file
@@ -0,0 +1,12 @@
|
||||
/* Linker script for STM32F030f4, 16K flash, 4K RAM. */
|
||||
|
||||
/* Define memory regions. */
|
||||
MEMORY
|
||||
{
|
||||
rom (rx) : ORIGIN = 0x08000000, LENGTH = 16K
|
||||
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 4K
|
||||
}
|
||||
|
||||
/* Include the common ld script. */
|
||||
INCLUDE stm32f0.ld
|
||||
|
||||
112
STM32/steppers/main.c
Normal file
112
STM32/steppers/main.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* main.c
|
||||
*
|
||||
* Copyright 2017 Edward V. Emelianoff <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "stm32f0.h"
|
||||
#include "usart.h"
|
||||
#include "adc.h"
|
||||
#include "flash.h"
|
||||
#include "proto.h"
|
||||
|
||||
volatile uint32_t Tms = 0;
|
||||
|
||||
/* Called when systick fires */
|
||||
void sys_tick_handler(void){
|
||||
++Tms;
|
||||
}
|
||||
|
||||
/*
|
||||
* PA4 (Tim14Ch1) M1STEP
|
||||
* PA6 (Tim3Ch1) M2STEP
|
||||
* PA5 - M2EN
|
||||
* PA7 - M2DIR
|
||||
* PB1 - 12V on/off
|
||||
* PF0 - M1EN
|
||||
* PF1 - M1DIR
|
||||
*/
|
||||
static void gpio_setup(void){
|
||||
// Enable clocks to the GPIO subsystems
|
||||
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOFEN;
|
||||
// PA0..3, PA13, PA14 - AIN; PA4..7 - PUPD;
|
||||
GPIOA->MODER = GPIO_MODER_MODER0_AI | GPIO_MODER_MODER1_AI | GPIO_MODER_MODER2_AI |
|
||||
GPIO_MODER_MODER3_AI | GPIO_MODER_MODER13_AI | GPIO_MODER_MODER14_AI |
|
||||
GPIO_MODER_MODER4_O | GPIO_MODER_MODER5_O | GPIO_MODER_MODER6_O | GPIO_MODER_MODER7_O;
|
||||
GPIOA->OSPEEDR = 0; // all low speed
|
||||
GPIOA->PUPDR = 0; // clear pull-down for PA14&PA13
|
||||
// PA4 - Tim14Ch1 (AF4), PA6 - Tim3Ch1 (AF1)
|
||||
GPIOA->AFR[0] = (GPIOA->AFR[0] &~ (GPIO_AFRL_AFRL4 | GPIO_AFRL_AFRL6))\
|
||||
| (4 << (4 * 4)) | (1 << (6 * 4));
|
||||
// PB1 - PUPD
|
||||
GPIOB->MODER = GPIO_MODER_MODER1_O;
|
||||
// PF0, PF1 - PUPD
|
||||
GPIOF->MODER = GPIO_MODER_MODER0_O | GPIO_MODER_MODER1_O;
|
||||
}
|
||||
|
||||
int main(void){
|
||||
uint32_t lastT = 0;
|
||||
uint32_t ostctr = 0;
|
||||
#if 0
|
||||
//def EBUG
|
||||
uint32_t msgctr = 0;
|
||||
#endif
|
||||
char *txt = NULL;
|
||||
sysreset();
|
||||
SysTick_Config(6000, 1);
|
||||
gpio_setup();
|
||||
adc_setup();
|
||||
USART1_config();
|
||||
get_userconf();
|
||||
//pin_set(GPIOA, 1<<5); // clear extern LED
|
||||
while (1){
|
||||
if(lastT > Tms || Tms - lastT > 499){
|
||||
#ifdef EBUG
|
||||
pin_toggle(GPIOA, 1<<4); // blink by onboard LED once per second
|
||||
#endif
|
||||
lastT = Tms;
|
||||
}
|
||||
if(usart1rx()){ // usart1 received data, store in in buffer
|
||||
if(usart1_getline(&txt)){
|
||||
txt = process_command(txt);
|
||||
}else txt = NULL; // buffer overflow
|
||||
}
|
||||
#if 0
|
||||
//def EBUG
|
||||
if(msgctr > Tms || Tms - msgctr > 4999){ // once per 5 seconds
|
||||
msgctr = Tms;
|
||||
txt = "hello";
|
||||
}
|
||||
#endif
|
||||
if(trbufisfull()){
|
||||
write2trbuf("ERR");
|
||||
usart1_send_blocking(gettrbuf());
|
||||
}
|
||||
cleartrbuf();
|
||||
if(txt){ // text waits for sending
|
||||
if(ALL_OK == usart1_send(txt)){
|
||||
txt = NULL;
|
||||
}
|
||||
}
|
||||
if(ostctr != Tms){
|
||||
ostctr = Tms;
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
282
STM32/steppers/proto.c
Normal file
282
STM32/steppers/proto.c
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* proto.c
|
||||
*
|
||||
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
#include "stm32f0.h"
|
||||
#include "proto.h"
|
||||
#include "adc.h"
|
||||
#include "flash.h"
|
||||
#include "string.h"
|
||||
#include "usart.h"
|
||||
|
||||
#define SENDBUF() do{usart1_send_blocking(gettrbuf()); cleartrbuf();}while(0)
|
||||
|
||||
static const char *eodata = "DATAEND";
|
||||
static const char *badcmd = "BADCMD";
|
||||
static const char *allok = "ALL OK";
|
||||
static const char *err = "ERR";
|
||||
|
||||
#define EODATA ((char*)eodata)
|
||||
#define BADCMD ((char*)badcmd)
|
||||
#define ALLOK ((char*)allok)
|
||||
#define ERR ((char*)err)
|
||||
|
||||
static char *getnum(char *buf, int32_t *N);
|
||||
static char *get_something(char *str);
|
||||
static char *set_something(char *str);
|
||||
static char *get_status();
|
||||
static char *get_conf();
|
||||
static char *get_raw_adc();
|
||||
static char *get_ADCval(char *str);
|
||||
static char *setDenEn(uint8_t De, char *str);
|
||||
static char *setDevId(char *str);
|
||||
static char *setESWthres(char *str);
|
||||
static char *get_temper();
|
||||
|
||||
#define omitwsp(str) do{register char nxt; while((nxt = *str)){if(nxt != ' ' && nxt != '\t') break; else ++str;}}while(0)
|
||||
|
||||
/**
|
||||
* get input buffer `cmdbuf`, parse it and change system state
|
||||
* @return message to send
|
||||
*/
|
||||
char* process_command(char *cmdbuf){
|
||||
int32_t num;
|
||||
char *str, c;
|
||||
#ifdef EBUG
|
||||
usart1_send_blocking(cmdbuf);
|
||||
#endif
|
||||
str = getnum(cmdbuf, &num);
|
||||
if(!str) return NULL; // bad format
|
||||
if(num != the_conf.devID && num != -1) return NULL; // other target
|
||||
// OK, the command is for this device
|
||||
while((c = *str)){if(c != ' ' && c != '\t') break; else ++str;}
|
||||
if(!c){ // simple ping
|
||||
return "ALIVE";
|
||||
}
|
||||
switch (*str++){
|
||||
case 'S': // set something
|
||||
return set_something(str);
|
||||
break;
|
||||
case 'G': // get something
|
||||
return get_something(str);
|
||||
break;
|
||||
case 'W': // write flash
|
||||
if(store_userconf()) return ERR;
|
||||
else return ALLOK;
|
||||
break;
|
||||
}
|
||||
return BADCMD; // badcmd
|
||||
}
|
||||
|
||||
// read `buf` and get first integer `N` in it
|
||||
// @return pointer to first non-number if all OK or NULL if first symbol isn't a space or number
|
||||
static char *getnum(char *buf, int32_t *N){
|
||||
char c;
|
||||
int positive = -1;
|
||||
int32_t val = 0;
|
||||
while((c = *buf++)){
|
||||
if(c == '\t' || c == ' '){
|
||||
if(positive < 0) continue; // beginning spaces
|
||||
else break; // spaces after number
|
||||
}
|
||||
if(c == '-'){
|
||||
if(positive < 0){
|
||||
positive = 0;
|
||||
continue;
|
||||
}else break; // there already was `-` or number
|
||||
}
|
||||
if(c < '0' || c > '9') break;
|
||||
if(positive < 0) positive = 1;
|
||||
val = val * 10 + (int32_t)(c - '0');
|
||||
}
|
||||
if(positive != -1){
|
||||
if(positive == 0){
|
||||
if(val == 0) return NULL; // single '-'
|
||||
val = -val;
|
||||
}
|
||||
*N = val;
|
||||
}else return NULL;
|
||||
return buf-1;
|
||||
}
|
||||
|
||||
// get conf (uint16_t) number
|
||||
// @return 0 if all OK
|
||||
static int getu16(char *buf, uint16_t *N){
|
||||
int32_t N32;
|
||||
if(!getnum(buf, &N32)) return 1;
|
||||
if(N32 > 0xffff || N32 < 0) return 1;
|
||||
*N = (uint16_t) N32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *get_something(char *str){
|
||||
switch(*str++){
|
||||
case 'A': // get ADC value: voltage or current
|
||||
return get_ADCval(str);
|
||||
break;
|
||||
case 'C': // get current configuration values
|
||||
return get_conf();
|
||||
break;
|
||||
case 'R': // get raw ADC values
|
||||
return get_raw_adc();
|
||||
break;
|
||||
case 'S': // get status
|
||||
return get_status();
|
||||
break;
|
||||
case 'T':
|
||||
return get_temper();
|
||||
break;
|
||||
}
|
||||
return BADCMD;
|
||||
}
|
||||
|
||||
static char *get_status(){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct{
|
||||
const char *fieldname;
|
||||
const uint16_t *ptr;
|
||||
} user_conf_descr;
|
||||
|
||||
static const user_conf_descr descrarr[] = {
|
||||
{"DEVID", &the_conf.devID},
|
||||
{"V12NUM", &the_conf.v12numerator},
|
||||
{"V12DEN", &the_conf.v12denominator},
|
||||
{"I12NUM", &the_conf.i12numerator},
|
||||
{"I12DEN", &the_conf.i12denominator},
|
||||
{"V33NUM", &the_conf.v33numerator},
|
||||
{"V33DEN", &the_conf.v33denominator},
|
||||
{"ESWTHR", &the_conf.ESW_thres},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static char *get_conf(){
|
||||
const user_conf_descr *curdesc = descrarr;
|
||||
write2trbuf("DATAPOS=");
|
||||
put_uint(the_conf.good_data_pos);
|
||||
SENDBUF();
|
||||
do{
|
||||
write2trbuf(curdesc->fieldname);
|
||||
put2trbuf('=');
|
||||
put_uint((uint32_t) *curdesc->ptr);
|
||||
SENDBUF();
|
||||
}while((++curdesc)->fieldname);
|
||||
return EODATA;
|
||||
}
|
||||
|
||||
static char *get_raw_adc(){
|
||||
int i;
|
||||
for(i = 0; i < NUMBER_OF_ADC_CHANNELS; ++i){
|
||||
write2trbuf("ADC[");
|
||||
put2trbuf('0' + i);
|
||||
write2trbuf("]=");
|
||||
put_uint((uint32_t) ADC_array[i]);
|
||||
SENDBUF();
|
||||
}
|
||||
return EODATA;
|
||||
}
|
||||
|
||||
static char *get_ADCval(char *str){
|
||||
uint32_t v;
|
||||
switch(*str){
|
||||
case 'D': // vdd
|
||||
write2trbuf("VDD=");
|
||||
v = getVdd();
|
||||
break;
|
||||
case 'I': // motors' current
|
||||
write2trbuf("IMOT=");
|
||||
v = getImot();
|
||||
break;
|
||||
case 'M': // vmot
|
||||
write2trbuf("VMOT=");
|
||||
v = getVmot();
|
||||
break;
|
||||
default:
|
||||
return BADCMD;
|
||||
}
|
||||
put_uint(v);
|
||||
SENDBUF();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *set_something(char *str){
|
||||
switch(*str++){
|
||||
case 'D': // set denominator
|
||||
return setDenEn(1, str);
|
||||
break;
|
||||
case 'E': // set numerator
|
||||
return setDenEn(0, str);
|
||||
break;
|
||||
case 'I': // set device ID
|
||||
return setDevId(str);
|
||||
break;
|
||||
case 'T': // set endsw threshold
|
||||
return setESWthres(str);
|
||||
break;
|
||||
}
|
||||
return BADCMD;
|
||||
}
|
||||
|
||||
/**
|
||||
* set denominator/numerator
|
||||
* @param De == 1 for denominator, == 0 for numerator
|
||||
* @param str - rest of string
|
||||
*/
|
||||
static char *setDenEn(uint8_t De, char *str){
|
||||
uint16_t *targ = NULL;
|
||||
switch(*str++){
|
||||
case 'D':
|
||||
targ = De ? &the_conf.v33denominator : &the_conf.v33numerator;
|
||||
break;
|
||||
case 'I':
|
||||
targ = De ? &the_conf.i12denominator : &the_conf.i12numerator;
|
||||
break;
|
||||
case 'M':
|
||||
targ = De ? &the_conf.v12denominator : &the_conf.v12numerator;
|
||||
break;
|
||||
default:
|
||||
return BADCMD;
|
||||
}
|
||||
omitwsp(str);
|
||||
if(getu16(str, targ)) return BADCMD;
|
||||
return ALLOK;
|
||||
}
|
||||
|
||||
static char *setDevId(char *str){
|
||||
omitwsp(str);
|
||||
if(getu16(str, &the_conf.devID)) return BADCMD;
|
||||
return ALLOK;
|
||||
}
|
||||
|
||||
static char *setESWthres(char *str){
|
||||
omitwsp(str);
|
||||
if(getu16(str, &the_conf.ESW_thres)) return BADCMD;
|
||||
return ALLOK;
|
||||
}
|
||||
|
||||
static char *get_temper(){
|
||||
uint32_t t = getTemp();
|
||||
write2trbuf("TEMP=");
|
||||
put_uint(t);
|
||||
SENDBUF();
|
||||
return NULL;
|
||||
}
|
||||
29
STM32/steppers/proto.h
Normal file
29
STM32/steppers/proto.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* proto.h
|
||||
*
|
||||
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef __PROTO_H__
|
||||
#define __PROTO_H__
|
||||
|
||||
char* process_command(char *cmdbuf);
|
||||
|
||||
#endif // __PROTO_H__
|
||||
37
STM32/steppers/steppers.geany
Normal file
37
STM32/steppers/steppers.geany
Normal file
@@ -0,0 +1,37 @@
|
||||
[editor]
|
||||
line_wrapping=false
|
||||
line_break_column=100
|
||||
auto_continue_multiline=true
|
||||
|
||||
[file_prefs]
|
||||
final_new_line=true
|
||||
ensure_convert_new_lines=true
|
||||
strip_trailing_spaces=true
|
||||
replace_tabs=true
|
||||
|
||||
[indentation]
|
||||
indent_width=4
|
||||
indent_type=0
|
||||
indent_hard_tab_width=4
|
||||
detect_indent=false
|
||||
detect_indent_width=false
|
||||
indent_mode=3
|
||||
|
||||
[project]
|
||||
name=Steppers
|
||||
base_path=/home/eddy/Docs/SAO/Zeiss-1000/Simple_photometer/STM32/
|
||||
|
||||
[long line marker]
|
||||
long_line_behaviour=1
|
||||
long_line_column=100
|
||||
|
||||
[files]
|
||||
current_page=4
|
||||
FILE_NAME_0=0;C;0;EUTF-8;0;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2Fmain.c;0;4
|
||||
FILE_NAME_1=130;Make;0;EUTF-8;1;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2FMakefile;0;4
|
||||
FILE_NAME_2=0;Markdown;0;EUTF-8;0;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2FReadme.md;0;4
|
||||
FILE_NAME_3=0;C;0;EUTF-8;0;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2Fusart.c;0;4
|
||||
FILE_NAME_4=0;C;0;EUTF-8;0;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2Fusart.h;0;4
|
||||
|
||||
[VTE]
|
||||
last_dir=/home/eddy
|
||||
218
STM32/steppers/usart.c
Normal file
218
STM32/steppers/usart.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* usart.c
|
||||
*
|
||||
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "usart.h"
|
||||
#include <string.h> // memcpy
|
||||
|
||||
extern volatile uint32_t Tms;
|
||||
|
||||
static int datalen[2] = {0,0}; // received data line length (including '\n')
|
||||
|
||||
int linerdy = 0, // received data ready
|
||||
dlen = 0, // length of data (including '\n') in current buffer
|
||||
bufovr = 0, // input buffer overfull
|
||||
txrdy = 1 // transmission done
|
||||
;
|
||||
|
||||
static int rbufno = 0; // current rbuf number
|
||||
static char rbuf[2][UARTBUFSZ], tbuf[UARTBUFSZ]; // receive & transmit buffers
|
||||
static char *recvdata = NULL;
|
||||
|
||||
static char trbuf[UARTBUFSZ]; // auxiliary buffer for data transmission
|
||||
int trbufidx = 0;
|
||||
int put2trbuf(char c){
|
||||
if(trbufidx > UARTBUFSZ - 1) return 1;
|
||||
trbuf[trbufidx++] = c;
|
||||
return 0;
|
||||
}
|
||||
// write zero-terminated string
|
||||
int write2trbuf(const char *str){
|
||||
while(trbufidx < UARTBUFSZ - 1 && *str){
|
||||
trbuf[trbufidx++] = *str++;
|
||||
}
|
||||
if(*str) return 1; // buffer overfull
|
||||
trbuf[trbufidx] = 0;
|
||||
return 0; // all OK
|
||||
}
|
||||
char *gettrbuf(){
|
||||
if(trbufidx > UARTBUFSZ - 1) trbufidx = UARTBUFSZ - 1;
|
||||
trbuf[trbufidx] = 0;
|
||||
return trbuf;
|
||||
}
|
||||
|
||||
|
||||
void USART1_config(){
|
||||
/* Enable the peripheral clock of GPIOA */
|
||||
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
|
||||
/* GPIO configuration for USART1 signals */
|
||||
/* (1) Select AF mode (10) on PA9 and PA10 */
|
||||
/* (2) AF1 for USART1 signals */
|
||||
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER9|GPIO_MODER_MODER10))\
|
||||
| (GPIO_MODER_MODER9_1 | GPIO_MODER_MODER10_1); /* (1) */
|
||||
GPIOA->AFR[1] = (GPIOA->AFR[1] &~ (GPIO_AFRH_AFRH1 | GPIO_AFRH_AFRH2))\
|
||||
| (1 << (1 * 4)) | (1 << (2 * 4)); /* (2) */
|
||||
// Tx (PA9) in OD mode
|
||||
GPIOA->OTYPER |= 1 << 9;
|
||||
#ifdef EBUG
|
||||
GPIOA->PUPDR = (GPIOA->PUPDR & ~GPIO_PUPDR_PUPDR9) | GPIO_PUPDR_PUPDR9_0; // set pullup for Tx
|
||||
#endif
|
||||
/* Enable the peripheral clock USART1 */
|
||||
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
|
||||
/* Configure USART1 */
|
||||
/* (1) oversampling by 16, 115200 baud */
|
||||
/* (2) 8 data bit, 1 start bit, 1 stop bit, no parity */
|
||||
USART1->BRR = 480000 / 1152; /* (1) */
|
||||
USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; /* (2) */
|
||||
/* polling idle frame Transmission */
|
||||
while(!(USART1->ISR & USART_ISR_TC)){}
|
||||
USART1->ICR |= USART_ICR_TCCF; /* clear TC flag */
|
||||
USART1->CR1 |= USART_CR1_RXNEIE; /* enable TC, TXE & RXNE interrupt */
|
||||
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
||||
DMA1_Channel2->CPAR = (uint32_t) &(USART1->TDR); // periph
|
||||
DMA1_Channel2->CMAR = (uint32_t) tbuf; // mem
|
||||
DMA1_Channel2->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // 8bit, mem++, mem->per, transcompl irq
|
||||
USART1->CR3 = USART_CR3_DMAT;
|
||||
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 3);
|
||||
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
|
||||
/* Configure IT */
|
||||
/* (3) Set priority for USART1_IRQn */
|
||||
/* (4) Enable USART1_IRQn */
|
||||
NVIC_SetPriority(USART1_IRQn, 0); /* (3) */
|
||||
NVIC_EnableIRQ(USART1_IRQn); /* (4) */
|
||||
}
|
||||
|
||||
void usart1_isr(){
|
||||
#ifdef CHECK_TMOUT
|
||||
static uint32_t tmout = 0;
|
||||
#endif
|
||||
if(USART1->ISR & USART_ISR_RXNE){ // RX not emty - receive next char
|
||||
#ifdef CHECK_TMOUT
|
||||
if(tmout && Tms >= tmout){ // set overflow flag
|
||||
bufovr = 1;
|
||||
datalen[rbufno] = 0;
|
||||
}
|
||||
tmout = Tms + TIMEOUT_MS;
|
||||
if(!tmout) tmout = 1; // prevent 0
|
||||
#endif
|
||||
// read RDR clears flag
|
||||
uint8_t rb = USART1->RDR;
|
||||
if(datalen[rbufno] < UARTBUFSZ){ // put next char into buf
|
||||
rbuf[rbufno][datalen[rbufno]++] = rb;
|
||||
if(rb == '\n'){ // got newline - line ready
|
||||
linerdy = 1;
|
||||
dlen = datalen[rbufno];
|
||||
recvdata = rbuf[rbufno];
|
||||
recvdata[dlen-1] = 0; // change '\n' to trailing zero
|
||||
// prepare other buffer
|
||||
rbufno = !rbufno;
|
||||
datalen[rbufno] = 0;
|
||||
#ifdef CHECK_TMOUT
|
||||
// clear timeout at line end
|
||||
tmout = 0;
|
||||
#endif
|
||||
}
|
||||
}else{ // buffer overrun
|
||||
bufovr = 1;
|
||||
datalen[rbufno] = 0;
|
||||
#ifdef CHECK_TMOUT
|
||||
tmout = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dma1_channel2_3_isr(){
|
||||
if(DMA1->ISR & DMA_ISR_TCIF2){ // Tx
|
||||
DMA1->IFCR |= DMA_IFCR_CTCIF2; // clear TC flag
|
||||
txrdy = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return length of received data (without trailing zero
|
||||
*/
|
||||
int usart1_getline(char **line){
|
||||
if(!linerdy) return 0;
|
||||
if(bufovr){
|
||||
bufovr = 0;
|
||||
linerdy = 0;
|
||||
return 0;
|
||||
}
|
||||
*line = recvdata;
|
||||
linerdy = 0;
|
||||
return dlen;
|
||||
}
|
||||
|
||||
/*
|
||||
// send bu UART zero-terminated string `str` with length `len` (with substitution of trailing zero by '\n')
|
||||
TXstatus usart1_send(char *str){
|
||||
if(!txrdy) return LINE_BUSY;
|
||||
int len = 0;
|
||||
while(len < UARTBUFSZ && str[len]) ++len;
|
||||
if(len > UARTBUFSZ-1) return STR_TOO_LONG;
|
||||
str[len++] = '\n';
|
||||
txrdy = 0;
|
||||
DMA1_Channel2->CCR &= ~DMA_CCR_EN;
|
||||
memcpy(tbuf, str, len);
|
||||
DMA1_Channel2->CNDTR = len;
|
||||
DMA1_Channel2->CCR |= DMA_CCR_EN; // start transmission
|
||||
return ALL_OK;
|
||||
}*/
|
||||
|
||||
TXstatus usart1_send(char *str){
|
||||
if(!txrdy) return LINE_BUSY;
|
||||
int i;
|
||||
for(i = 0; i < UARTBUFSZ; ++i){
|
||||
char c = *str++;
|
||||
if(c == 0){ c = '\n'; i = UARTBUFSZ;}
|
||||
USART1->TDR = c;
|
||||
while(!(USART1->ISR & USART_ISR_TXE));
|
||||
}
|
||||
txrdy = 1;
|
||||
return ALL_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fill trbuf with integer value
|
||||
* @param N - integer value
|
||||
* @return 1 if buffer overflow; oterwise return 0
|
||||
*/
|
||||
int put_int(int32_t N){
|
||||
if(N < 0){
|
||||
if(put2trbuf('-')) return 1;
|
||||
N = -N;
|
||||
}
|
||||
return put_uint((uint32_t) N);
|
||||
}
|
||||
|
||||
int put_uint(uint32_t N){
|
||||
char buf[10];
|
||||
int L = 0;
|
||||
if(N){
|
||||
while(N){
|
||||
buf[L++] = N % 10 + '0';
|
||||
N /= 10;
|
||||
}
|
||||
while(L--) if(put2trbuf(buf[L])) return 1;
|
||||
}else if(put2trbuf('0')) return 1;
|
||||
return 0;
|
||||
}
|
||||
60
STM32/steppers/usart.h
Normal file
60
STM32/steppers/usart.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* usart.h
|
||||
*
|
||||
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef __USART_H__
|
||||
#define __USART_H__
|
||||
|
||||
#include "stm32f0.h"
|
||||
|
||||
// input and output buffers size
|
||||
#define UARTBUFSZ (64)
|
||||
// timeout between data bytes
|
||||
#define TIMEOUT_MS (1500)
|
||||
// check timeout
|
||||
#define CHECK_TMOUT
|
||||
|
||||
typedef enum{
|
||||
ALL_OK,
|
||||
LINE_BUSY,
|
||||
STR_TOO_LONG
|
||||
} TXstatus;
|
||||
|
||||
#define usart1rx() (linerdy)
|
||||
#define usart1ovr() (bufovr)
|
||||
|
||||
extern int linerdy, bufovr, txrdy;
|
||||
extern int trbufidx;
|
||||
|
||||
void USART1_config();
|
||||
int usart1_getline(char **line);
|
||||
TXstatus usart1_send(char *str);
|
||||
#define usart1_send_blocking(str) do{}while(LINE_BUSY == usart1_send(str))
|
||||
|
||||
#define cleartrbuf() do{trbufidx = 0;}while(0)
|
||||
#define trbufisfull() (trbufidx)
|
||||
int put2trbuf(char c);
|
||||
int write2trbuf(const char *str);
|
||||
char *gettrbuf();
|
||||
int put_int(int32_t N);
|
||||
int put_uint(uint32_t N);
|
||||
|
||||
|
||||
#endif // __USART_H__
|
||||
Reference in New Issue
Block a user