start of STM32 programming (very-very pre-alpha version)

This commit is contained in:
eddyem
2017-12-05 22:12:09 +03:00
parent a6e198ec78
commit 22d195d38b
26 changed files with 11244 additions and 0 deletions

137
STM32/steppers/Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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__

View 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
View 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
View 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
View 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__

View 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
View 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
View 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__