STM32 + linux

    To develop a control system for one piece of iron after a long search, I selected an STM32 ARM microcontroller - STM32F103 (in the “centipede” version). And as a breadboard for development and debugging - STM32P103 (although there are fewer legs, but the core is the same). I laid out "Success Stories" in my ZhZhshka little by little, but I decided to put it all together and talk about how it feels to program microcontrollers in Linux. The project itself lies with sourceforge .



    First of all, I’ll touch on the general, and then I will go on to the details.

    So, in addition to the breadboard model (or the finished device - when it is ready), you will need a JTAG adapter. In my case, this is ST-LINK / V2. Naturally, iron alone is not enough: you still need to compile the code somehow, and then upload it to the controller. For this, the gcc compiler for ARM (arm-none-eabi) and the utility for working with ST-LINK (it is called stlink) were installed.

    As a sample, I took this project . From here I downloaded simple demo projects and tried to compile the simplest one. And the very first was the standard Helloworld for MK: LED blinking.

    I’ll say right away which rake I ran into from the very beginning: I forgot about objcopy, without which it will be difficult to get working code. After compiling the project, you must definitely create a binary using this utility. And do not throw out Makefiles of incomprehensible (and seemingly even unnecessary at first glance) goals.

    As a convenient IDE I use Geany. Since I have two monitors at work, it’s quite convenient to work: on one monitor I have Geany open with code, and on the second - the terminal, where I run make and com (the terminal from tinyserial).

    I will not consider the entire Makefile, I will only pay attention to what should be changed in it:
    The code
    BIN=testproject
    …
    STM32_LIBSRC+=stm32_lib/misc.c
    STM32_LIBSRC+=stm32_lib/stm32f10x_adc.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_bkp.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_can.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_cec.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_crc.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_dac.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_dbgmcu.c
    STM32_LIBSRC+=stm32_lib/stm32f10x_dma.c
    STM32_LIBSRC+=stm32_lib/stm32f10x_exti.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_flash.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_fsmc.c
    STM32_LIBSRC+=stm32_lib/stm32f10x_gpio.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_i2c.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_it.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_iwdg.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_pwr.c
    STM32_LIBSRC+=stm32_lib/stm32f10x_rcc.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_rtc.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_sdio.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_spi.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_tim.c
    STM32_LIBSRC+=stm32_lib/stm32f10x_usart.c
    #~ STM32_LIBSRC+=stm32_lib/stm32f10x_wwdg.c
    …
    SRC=hw_config.c  main.c leds.c  interrupts.c  usb_desc.c usb_istr.c \
    	usb_prop.c  usb_pwr.c onewire.c
    …
    #~ OBJ+=stm32f10x_bkp.o
    #~ OBJ+=stm32f10x_can.o
    #~ OBJ+=stm32f10x_cec.o
    #~ OBJ+=stm32f10x_crc.o
    #~ OBJ+=stm32f10x_dac.o
    #~ OBJ+=stm32f10x_dbgmcu.o
    OBJ+=stm32f10x_dma.o
    OBJ+=stm32f10x_exti.o
    #~ OBJ+=stm32f10x_flash.o
    #~ OBJ+=stm32f10x_fsmc.o
    OBJ+=stm32f10x_gpio.o
    #~ OBJ+=stm32f10x_i2c.o
    #~ OBJ+=stm32f10x_it.o
    #~ OBJ+=stm32f10x_iwdg.o
    #~ OBJ+=stm32f10x_pwr.o
    OBJ+=stm32f10x_rcc.o
    #~ OBJ+=stm32f10x_rtc.o
    #~ OBJ+=stm32f10x_sdio.o
    #~ OBJ+=stm32f10x_spi.o
    #~ OBJ+=stm32f10x_tim.o
    OBJ+=stm32f10x_usart.o
    #~ OBJ+=stm32f10x_wwdg.o
    

    • BIN - name of the resulting binary after compilation
    • STM32_LIBSRC and OBJ contain STDPeriphLib library plug-ins, unused ones must be commented out
    • SRC contains a list of user sources


    After the code is written, run make. If everything is in order, the $ (BIN) .bin file will appear in the current directory, which you need to write to the USB flash drive. Recording is done using make load: this build target simply calls st-flash to flash the microcontroller.

    USB


    So, first of all, you need to establish a connection between the computer and the PC. Considering that RS-232 is absent in modern computers, I will organize debugging communication via USB. However, in the “combat conditions”, the MC will receive RS-232 commands from another controller, so I decided to immediately look towards the organization of the emulator adapter USB <-> RS-232. This approach is also convenient because you don’t have to bother with extra code to interact with the device via USB (although this is elementary, but too lazy!). And debugging is simple: open the device / dev / ttyACM0 as a serial port using any serial terminal emulator and "communicate". Yes, as a terminal emulator at first (there is no software from the computer yet) I used tinyserial.

    From hereI downloaded the code of the emulator adapter USB <-> RS-232. Since I couldn’t immediately check the operability of the second side (RS-232) (there is nowhere to connect), the unused USART operation code was temporarily commented out.

    To work with USB, a library from STMicroelectronics is used. If you don’t delve into the codes of the library itself, everything is pretty simple: we need to redefine the descriptors for our hardware (usb_desc. [Ch] files) so that the computer recognizes it as a USB <-> RS-232 adapter, and change the interrupt handlers to USB events (at least - process the received data, and for transparent operation as an adapter, you will also need to add USART interrupt processing to transfer data received from there via USB).

    To send messages, we use something like a ring buffer, which will gradually fill up, and, if necessary, will be transmitted via USB. We will read the data in the "normal buffer". Since for now I use only short commands, I did not bother with processing long packages. If they are, it will be necessary to complicate the interrupt handler for receiving data from USB a little.

    Since some commands (for example, reading temperature from 1-wire sensors) take quite a long time, the handler for USB commands only modifies flags for such operations, and the main loop in main () processes these flags. Operations that are performed quickly (work with the LED) are called directly from this function. For debugging purposes, I added an “echo” to the commands in the form of a brief decryption of it:
    The code
    void usb_handle_command(uint16_t cnt){
    	uint8_t command, *answer;
    	uint16_t i;
    	for(i = 0; i < cnt; i++){
    		command = USB_Rx_Buffer[i];
    		switch(command){
    			case CMD_LED_ON:
    				LED_On();
    				answer = (uint8_t*)"On";
    			break;
    			case CMD_LED_OFF:
    				LED_Off();
    				answer = (uint8_t*)"Off";
    			break;
    			case CMD_LED_BLINK:
    				LED_OnBlink();
    				answer = (uint8_t*)"Blk";
    			break;
    			case CMD_LED_DUTY_PLUS:
    				LED_DutyPlus();
    				answer = (uint8_t*)"Shn";
    			break;
    			case CMD_LED_DUTY_MINUS:
    				LED_DutyMinus();
    				answer = (uint8_t*)"Fad";
    			break;
    			case CMD_1W_GET_TEMP:
    				FLAGS |= FLAG_READ_T;
    				answer = (uint8_t*)"Read T";
    			break;
    			case CMD_1W_GET_DEV:
    				FLAGS |= FLAG_GETDEV;
    				answer = (uint8_t*)"find devices";
    			break;
    			case CMD_1W_PRNT_DEV:
    				FLAGS |= FLAG_PRINTDEV;
    				answer = (uint8_t*)"Print devices";
    			break;
    			case CMD_HALL_GET:
    				FLAGS |= FLAG_PRINTHALL;
    				answer = (uint8_t*)"Print Hall";
    			break;
    			case CMD_ADC_GET:
    				FLAGS |= FLAG_PRINTADC;
    				answer = (uint8_t*)"Print ADC val";
    			break;
    			default:
    				answer = (uint8_t*)"Unk";
    		}
    		newline();
    		prnt(answer);
    		newline();
    	}
    }
    


    That's it, now when you connect the breadboard model to the computer via USB (and it, in fact, is always connected to me, because it is powered via USB), the device / dev / ttyACM0 appears, which you can work with like a regular serial port. For example, open it using a serial terminal (as I said above, at first I use tinyserial).

    LED button


    It’s probably traditional to “blink with a diode” at the beginning of studying some new piece of hardware, so I’ll do the same. And at the same time I will hang up an interrupt on the “user button”, which will change the operating modes of the LED.

    Just blinking is not interesting: it is interesting to change the brightness. For this, a simple “soft” PWM is enough. Set the SysTick timer for a period of 10 μs. We will start two counters: one for the number of “ticks” during which the LED is on, and the second for the number of “ticks” during which the LED is off. To change the brightness of the LED, I made a simple eight-level scheme for changing the PWM duty cycle.
    This is what happened:
    The code
    uint8_t LED_GetState(){
    	return led_state;
    }
    void LED_Duty(uint8_t duty){
    	duty_cycle = duty;
    	if(led_state == LEDSTATE_BLINK)
    		LED_OnBlink();
    }
    void LED_DutyPlus(){
    	if(duty_cycle < 7) duty_cycle++;
    	if(led_state == LEDSTATE_BLINK)
    		LED_OnBlink();
    }
    void LED_DutyMinus(){
    	if(duty_cycle > 0) duty_cycle--;
    	if(led_state == LEDSTATE_BLINK)
    		LED_OnBlink();
    }
    uint8_t LED_GetBlinkState(uint16_t *blink_on, uint16_t *blink_off){
    	*blink_on = led_blink_on;
    	*blink_off = led_blink_off;
    	return led_state;
    }
    void LED_On(){
    	led_state = LEDSTATE_ON;
    	led_blink_on = 0;
    	led_blink_off = 0;
    	GPIO_ResetBits(GPIOC, GPIO_Pin_12);
    }
    void LED_Off(){
    	led_state = LEDSTATE_OFF;
    	GPIO_SetBits(GPIOC, GPIO_Pin_12);
    }
    void LED_OnBlink(){
    	led_blink_off = 1 << duty_cycle;
    	led_blink_on = 0xff - led_blink_off;
    	led_ticks_on = 0;
    	led_ticks_off = 0;
    	if(led_blink_off == 0){
    		LED_On();
    		return;
    	}
    	if(led_blink_on == 0)
    	{
    		LED_Off();
    		return;
    	}
    	led_state = LEDSTATE_BLINK;
    }
    void LED_SysTick_Handler(){
    	if(led_state != LEDSTATE_BLINK) return;
    	if(led_ticks_on == 0)
    		GPIO_SetBits(GPIOC, GPIO_Pin_12);
    	if(led_ticks_on <= led_blink_on)	{
    		led_ticks_on++;
    		return;
    	}
    	if (led_ticks_off == 0){
    		GPIO_ResetBits(GPIOC, GPIO_Pin_12);
    	}
    	if(led_ticks_off <= led_blink_off){
    		led_ticks_off++;
    		return;
    	}
    	led_ticks_on = 0;
    	led_ticks_off = 0;
    }
    


    On the "user button" I hung an external interrupt:
    The code
    	// Enable the BUTTON Clock
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	// Configure Button pin as input
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	// Connect Button EXTI Line to Button GPIO Pin
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
    	// Configure Button EXTI line
    	EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    	EXTI_Init(&EXTI_InitStructure);
    	// Enable and set Button EXTI Interrupt to the lowest priority
    	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStructure);
    

    And its handler is engaged in the fact that it puts the LED in the continuous light mode if it “blinked”, or vice versa - in the “blink” mode if it was lit:
    The code
    void EXTI0_IRQHandler(void){
    	if(EXTI_GetITStatus(EXTI_Line0) != RESET){
    		if(LED_GetState() != LEDSTATE_BLINK)
    			LED_OnBlink();
    		else
    			LED_On();
    		EXTI_ClearITPendingBit(EXTI_Line0);
    	}
    }
    


    1-wire


    I stole the code for working with 1-wire from somewhere from easyelectronics.ru . I changed it quite a bit. First of all, I changed the search function for devices hanging on the bus (for some reason, it didn’t work in the original, although the logic seemed to be quite clear and correct).

    In the example I stole, 1-wire worked through USART, and DMA was used for reading / writing. I really liked this idea, so I used it in this way (although it was possible to organize the 1-wire program protocol as well).

    The standard scheme for connecting a 1-wire bus to a serial port implies the presence of a Schottky diode:

    However, I did not have such a diode. But I realized that in addition to the push-pull mode, the leg responsible for the USART_TX can be switched to open-drain mode - in this case there will be no short circuit. To work with 1-wire, I used USART3 (while I play, I have enough legs - so I do not need to remap). In the diagram, I saw that the USART3 legs (PB10 and PB11) are already pulled to the ground through 10kΩ resistors, so I didn’t even have to solder the resistor: I just soldered a small scarf with sockets on the breadboard so that it was convenient to connect the temperature sensors.

    I will not describe in detail the contents of the onewire.c file: this has already been done to me many times, but I will only touch on working with thermometers directly.

    To monitor the temperature of the warm (above -50 ° C) parts of the device, I decided to use simple DS18S20 sensors (the stated measurement accuracy is no worse than 0.5 ° C). I connected the socket soldered to the breadboard to the necessary conclusions, so that a couple of thermometers could be connected to the MC at the same time.

    Here, for example, what I get when working with thermometers:
    The code
    com /dev/ttyACM0
    C-a exit, C-x modem lines status
    [STATUS]: RTS CTS DTR
     // жму 'c' :
    find devices
    Found 2 devices
     // жму 'p' :
    Print devices
    device 0:
    0x10 0x7c 0xee 0x8f 0x02 0x08 0x00 0x1c
    device 1:
    0x10 0xad 0xbc 0x8f 0x02 0x08 0x00 0xf9
     // жму 't' :
    Read T
    Device 0: 0x3b 0x00 0x4b 0x46 0xff 0xff 0x08 0x10 0x39
    Device 1: 0x3a 0x00 0x4b 0x46 0xff 0xff 0x0c 0x10 0x41
    


    To begin with, I marked all the thermometers to know which identifier anyone has. And then I decided to see if their testimonies vary greatly. Temperature - the first two bytes of the sensors response. In the seventh byte is stored the "remainder" from the temperature conversion built into the ADC thermometer. According to the documentation for the sensors, this residue helps to clarify the temperature value. However, it turned out that the good of him was like a goat's milk.

    In the process, the sensor heats up itself, which affects the measurement results. Therefore, do not interrogate them too often. In addition, the readings of the sensors differed from each other up to one and a half degrees! This should be borne in mind: if you plan to use several sensors in such a way as to monitor the temperature difference between sections of something with an accuracy of no worse than 0.5 ° C, you must first calibrate all the sensors. And take readings according to calibration formulas, and not the response of the sensors.

    The actual sensor error sometimes exceeds 0.5 ° C, so it’s still better to assume that the sensor has an accuracy of 1 ° C.

    Hall Sensor


    I have analog Hall sensors - SS495A. Sensor specifications can be found on the Internet. I can only say that in the normal state on its output leg the voltage is about 2.5V (logical unit STM32), depending on the polarity and the magnitude of the external magnetic field, it will change its readings within 0..5V. Given that the output voltage can reach five volts, it is necessary to use not the usual, but “five-volt” (designated as FR in the specification) controller inputs.

    The magnets I have (especially for this sensor) when placing their working surface within 1 mm of the sensor “face” (marked side) led to the appearance of zero voltage at its output. Moreover, the level of logical zero appears in a rather small area along the coordinates in the parallel marked side of the plane sensor, i.e. positioning accuracy is pretty decent.

    For experiments, I soldered one sensor on a breadboard. He connected the power to 5V, and the signal output led to the PC10 port, which will not burn if 5V is applied to it. In order not to pull the port constantly, I hung up an interrupt on it (similar to the button). The interrupt handler simply sets the appropriate flag, and in the main loop, if this flag is set (that is, the magnet either appeared or left the sensor’s “field of view”), we check that we have PC10. If there is zero (there is MP), write to the terminal “Magnet”, otherwise we write “clear”. You can also force-check whether there is a sensor or not by pressing “h” in the terminal.

    ADC


    In addition to the “warm zones” I will still need to measure the temperature in the cold (up to 75K from above). For this, platinum thermal resistance connected to the ADG506A analog switch will be used. Well, of course, I was wondering how bad the “native” MSC converter is: can it be used to measure temperature?

    The STM32 examples of working with the ADC are full, I took an example from STDPeriphLib. We will run the ADC in continuous conversion mode, and the result will be stored in memory using DMA. I set the conversion time to the largest (to be more precise), and for the time being I will hang the ADC input PB0 (ADC8):
    The code
    	// 0. Configure ADC8 (PB0) as analog input (clocking GPIOB sat on in onewire.c)
    	RCC_ADCCLKConfig(RCC_PCLK2_Div4);
    	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOB, ENABLE);
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    	GPIO_Init(GPIOB, &GPIO_InitStructure);
    	// 1. DMA for converted value (DMA1 clocking sat on at onewire.c)
    	//RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    	DMA_DeInit(DMA1_Channel1);
    	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
    	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_value;
    	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    	DMA_InitStructure.DMA_BufferSize = 1;
    	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
    	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    	DMA_Cmd(DMA1_Channel1, ENABLE);
    	// 2. ADC1 config
    	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    	ADC_InitStructure.ADC_NbrOfChannel = 1;
    	ADC_Init(ADC1, &ADC_InitStructure);
    	// Connect ADC to ADC8 (PB0),
    	ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_239Cycles5);
    	// Enable ADC1 DMA
    	ADC_DMACmd(ADC1, ENABLE);
    	ADC_Cmd(ADC1, ENABLE);
    	// Calibration of ADC1
    	ADC_ResetCalibration(ADC1);
    	while(ADC_GetResetCalibrationStatus(ADC1));
    	ADC_StartCalibration(ADC1);
    	while(ADC_GetCalibrationStatus(ADC1));
    	ADC_SoftwareStartConvCmd(ADC1, ENABLE); // turn conversion on
    


    To work with the switch, you need to configure five bits of the control port. In order not to worry about bit conversion, I just took the first four bits of port C as the address, and the fifth bit as the key that includes the switch:
    The code
    	GPIO_InitStructure.GPIO_Pin = 0x1f; // first 5 bits of PC0
    	// PC0..PC3 - analog channel address, PC4 - analog enable switch
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    	GPIO_Init(GPIOC, &GPIO_InitStructure);
    


    There are no interruptions here, but in the interrupts.c file you need to add the setting of a new flag when a command (say, command 'a') displays the voltage on the sensors. In main (), add the processing of this flag:
    The code
    inline void prntADC(){
    	uint32_t address; // addr = 0, EN = 1
    	uint8_t *_2b = (uint8_t *) &ADC_value;
    	for(address = 0x10; address < 0x20; address++){
    		// changhe channel address & turn on switch
    		GPIOC->BSRR = address;
    		Delay(2); // wait for AD conversion
    		prnt((uint8_t*)"Temperature ");
    		printInt(address&0x0f); prnt((uint8_t*)" = ");
    		printInt(_2b[1]);
    		printInt(_2b[0]);
    		newline();
    		// turn off switch & reset bits
    		GPIOC->BRR = (uint32_t)0x1f;
    		Delay(2);
    	}
    }
    

    When a command arrives in a cycle, we start setting the desired address on the switch, wait a couple of milliseconds for the ADC to work, and then display the resulting value. Then we turn off the switch and wait (just in case) for a couple more milliseconds.

    On a separate breadboard, I assembled a simple resistive voltage divider, connecting all the analog inputs of the switch with small-ohm (200..900 Ohm) resistors. I connected the ground to S1, and + 3.3V from the STM32 prototype to S16. I powered the chip with an old PSU from an external HDD (12V).

    In the STM32P103 breadboard, the reference voltage for the ADC is taken from the general power supply, so the accuracy turned out to be low: sometimes the values ​​float as much as 20 units!

    Here, for example, what happened with two polls:
    The code
    // опрос 1
    Temperature 0x00  = 0x00 0x00
    Temperature 0x01  = 0x00 0x84
    Temperature 0x02  = 0x00 0xaf
    Temperature 0x03  = 0x01 0xdb
    Temperature 0x04  = 0x03 0x10
    Temperature 0x05  = 0x03 0xe4
    Temperature 0x06  = 0x05 0xca
    Temperature 0x07  = 0x06 0x9b
    Temperature 0x08  = 0x07 0x4e
    Temperature 0x09  = 0x08 0xd6
    Temperature 0x0a  = 0x0a 0x04
    Temperature 0x0b  = 0x0a 0xb4
    Temperature 0x0c  = 0x0b 0xfc
    Temperature 0x0d  = 0x0d 0xe0
    Temperature 0x0e  = 0x0e 0xb7
    Temperature 0x0f  = 0x0f 0xff
    // опрос 2
    Temperature 0x00  = 0x00 0x00
    Temperature 0x01  = 0x00 0x7f
    Temperature 0x02  = 0x00 0xaf
    Temperature 0x03  = 0x01 0xdf
    Temperature 0x04  = 0x03 0x0f
    Temperature 0x05  = 0x03 0xe4
    Temperature 0x06  = 0x05 0xcc
    Temperature 0x07  = 0x06 0x9d
    Temperature 0x08  = 0x07 0x5a
    Temperature 0x09  = 0x08 0xd6
    Temperature 0x0a  = 0x0a 0x01
    Temperature 0x0b  = 0x0a 0xb5
    Temperature 0x0c  = 0x0b 0xfc
    Temperature 0x0d  = 0x0e 0x09
    Temperature 0x0e  = 0x0e 0xb0
    Temperature 0x0f  = 0x0f 0xec
    


    In general, it will be necessary either to try to add a stable source of the reference voltage (and the measured circuit should be supplied from there), or to use an external ADC in general. Given the low resistance of the sensors that will be used, you still have to solder an amplifier.

    Stepper motor


    I haven’t finished messing with the stepman since I suspect that when installing elements on a breadboard, nothing will “take off” from me. It is necessary to solder. And I’ll probably deal with soldering only next year (I still need to buy some radio components). So far, only briefly tell you how I plan to control stepper motors.

    I will have step-changers - VSS42 at 1.2 amperes. These are most conveniently managed using the ШД driver - L6208. When working on this microcircuit, it is only necessary to supply directional control signals, a work enable signal and clock pulses. The controller itself regulates the PWM and sets the desired voltage on the motor windings.

    I will point out the main thing to pay attention to:
    • PWM adjustment is done by comparing the voltage drop across the Sense resistors with the reference voltage Vref. Therefore, for the current I max and the resistance of the resistors R Sense, this drop can be calculated quite simply:
      U ref = I max · R Sense
      i.e. To set the current limit to 1.2 A at R Sense = 0.33 Ohm, you need to set U ref = 0.4 V. In no case should you leave your legs V ref hanging in the air or pressed to the ground!
    • Slow / Fast decay mode is important for conventional collector motors, while step deciders need Fast decay only in microstepping mode. In general, if you do not pervert, it is enough just to apply + 5V to the CONTROL leg. On HALF / FULL, we also simply apply + 5V and work in half-step mode. We do the same with the RESET foot if we do not want to reset the phase counter (and we do not need to reset it, to be honest, give 8 clock pulses for each step).
    • ENABLE input is also an output: if a trouble occurs with the L6208 driver (overheating, current surge), it independently disconnects the voltage at the load, and ENABLE pulls it to the ground. This means that you can check if an emergency has occurred if the foot of the controller that controls the ENABLE port is activated in open collector output mode.
      According to the STM32 specification, in the open collector mode, when a unit is fed to the port output, the transistor simply lifts the leg to the ground. If the output is zero, then the leg is again pulled.
      Thus, pulling the foot of the controller to + 5V (you need to select FT for the foot ) through, say, a five-kilo resistor, and between it and ENABLE, stick, say, a kilo-resistor (and be sure todo not forget to shunt the leg of ENABLE with a capacitor to the ground , otherwise you can burn the control controller), you can turn on / turn off the desired engine and check if there are any accidents (for this you can hang peripheral interruption on the falling legs of the controller).
    • Conductors marked in bold in the specification should be as short and wide as possible. But at the same time, care must also be taken to reduce stray capacitances and inductances.
      R Sense should be located as close as possible to the driver. Not far from them there should be capacitors C₁ and C₂ (according to the specification): not only the comparison current flows through this circuit, but also the reverse induction current (therefore, by the way, diodes cannot be included in this chain ). Connect the
      signal ground to the power ground only further than the connection point C₁ to the ground, otherwise the induction current can spoil the electronics simply by dropping the voltage on the print wires (it’s true, it’s not how it was possible, but it’s better to listen to the safety requirements).
    • I also read an interesting thing: power supply cannot be connected without a signal ! The documentation recommends that you even take a voltage of 5 V from a power source using a stabilizing unit.
      I honestly did not understand anything: actually +5 V is supplied only to the driver's control legs. If we do not use the engine, then, in theory, it can have 0 on all signal inputs. But I will still turn on the power supply after turning on the power to the controller. And turn off in the reverse order. And make sure that the USB-cable supplying the controller does not pop out ...

    Also popular now: