Microprocessor Microprocessor Notes of Beiyou State Academy of Things

Introduction-Casual chat

What is an embedded system? Dedicated computer systems. Some trade-offs may be made in computer architecture, peripherals, etc. for specialized functions.

Usual limitations: Cost (such as deploying a large number of sensor nodes), Size and weight limits (specific application scenarios, such as sewer flow detection systems, which require small nodes), Power and energy limits (such as deployment in extreme environments, Himalayan mountain top collection node, inconvenient to charge), Environment (waterproof, high temperature resistance, etc.)

The difference between the two embedded systems of MCU and MPU: focus on control or processing. Controls such as lighting, robotic arms, and motors are all. Processing, such as the data collected by the camera for image processing.

Programming language: close to the bottom of the computer, mainly using assembly and c.

OS: There is not necessarily an operating system structure in an embedded system. To put it bluntly, the operating system is to better help manage computer resource scheduling. Now let's analyze the main function of our lab2 code:

void main(){
    
    
    //background
    while(1){
    
    
        
    }
}

void IRQ_Handler(){
    
    
    //interrupt handler function, frontground
}

Background part: a loop that repeatedly performs the tasks to be done.

This approach looks fine at first glance. But think about what such a computer can do, it can only perform all tasks over and over again in order, and there is no way to change the order.

Foreground part: interrupt processing, the uart_rx_isr function in our lab2 generally uses IRQ_Handler (in fact, if you trace the source of uart_rx_isr in lab2, you will find that it is actually called by IRQ_Handler, this method triggers the interrupt when the corresponding interrupt is started will be called automatically).

The combined system of the front and back is still a bare-metal OS-free system, but after the interrupt is added, it allows us to use the interrupt task to interrupt the background polling and change the execution order. For example, when the number of serial port interrupts is sent, the CPU puts aside the background work at hand, handles the foreground interrupts, and returns after processing.

Our courses are limited to content developed on bare metal.

A brief introduction to computer systems

Von Neumann Architecture

Arithmetic unit controller (integrated in CPU) memory main memory input device output device IO, and three transmission buses: data, control, address data bus / control bus / address bus.

1686650488954

As mentioned above, MPU focuses on data calculation and processing, while MCU is on control, so MPU does not need some peripherals to control external components.

Harvard Architecture

The difference from von Neumann is that instructions and data are stored separately. In this way, the efficiency of index-seeking, index-fetching, and number-fetching is high.

Stored Program Concept

There are two main parts: RAM stores programs and data, and ROM stores unchanged read-only programs and data.

The cpu execution instruction is the repeated execution of three steps: fetch decode execute fetch instruction decode execution

assembly

If high-level language is equivalent to translating human words to computers, assembly language is equivalent to translating computer language to us. It is closer to the bottom layer, so the operating efficiency is higher, and the hardware can be directly operated.

ADD r3, r1, r2 	;r3 = r1 + r2
SUB r3, r0, r3 
MOV r2, r1 		;r2 = r1

; is a comment. The variable r123 is a register register, which is a part that can manipulate the hardware, and we can manipulate the hardware by assigning a value to it.

High-level language is translated into assembly language through compiler, and assembly language is translated into binary machine language through assembler.

ARM architecture

ARM is an instruction set, and the assembly instructions mentioned above are all counted as instructions.

The interesting thing about ARM is that they don't make ARM devices, they only design the instruction set architecture, and then authorize (intellectual property cores, IP cores) to other semiconductor manufacturers.

A: application, which focuses on high performance, and many mobile computers are based on ARM architecture.

R: realtime, focusing on real-time. For example, the Internet of Vehicles has high requirements for real-time performance.

M: microcontroller, used in small embedded systems, the board we use.

The m series has m0 to m7 (simply speaking, the performance gradually increases?), and it is backward compatible, that is, m7 is compatible with m0~m6.

SoC

There is a small black chip on our board with a string of characters stm32blabla written on it. This is the core of the entire board, which is equivalent to the chip structure that includes the computer architecture mentioned above, system on chips.

1686658267695

Soc design rules: first select the IP core, design the ARM processor, plus a series of storage and IO peripheral structures, all integrated on the black chip.

The ARM processor processor is a specific coverage of the architecture, and there are many new contents such as timers.

We mainly learn the m4 architecture.

1686658682835

Just look at the non-optional for an overview, the processor core access code, and the data interface.

register

We have briefly introduced register earlier. In fact, if you want to process the data in the memory, you must first get it into the register in the processor core to do the calculation, and then return it back.

The arm register is as follows:

1686659016141

General-purpose registers: temporary variables that can store calculation data and the like.

SP: Stack top pointer register, pointing to the top of the stack.

LR: For function return, save the return address. For example, to call a function, store the value of PC into LR, and then PC jumps to the starting position of the function; when the function returns, the value of LR is returned to PC.

PC: Points to the program counter where the program is currently executing (the address of the next instruction to be executed). After each instruction is fetched, the PC automatically adds an instruction, such as the 32-bit instruction set PC+=4B.

The PSR series are status registers that indicate the current program status. For example, is the current user mode or kernel mode? IPSR indicates whether interrupts are currently enabled? wait.

xPSRs include:

  • APSR: For calculation, such as whether the flag carries, whether the result is 0, whether it is negative, whether it overflows, etc.
  • IPSR: Interrupt handling related.
  • EPSR: Execution-related, indicating the instruction set, whether the interruption continues, and other information.

1686659814546

Memory Map

The m4 has 4g of memory space mapped to a space by default, and users can also modify it according to their preferences. There are code region for storing code, sram region for storing data, peripheral region for storing peripherals, external ram region, external device region, Internal Private Peripheral Bus (PPB).

1686669411390

Bit-band Operations

bit band operation.

If we want to read and write a certain bit of 32-bit data, such as the third bit (31:0 from left to right, the third bit is the 4th on the right), some registers allow us to directly get r[3], But most of them are not allowed to be obtained directly.

How to deal with it? If 1 is written, then r|0000 0000 0000 0000 0000 0000 0000 1000.

If write 0, then r & 1111 1111 1111 1111 1111 1111 1111 0111.

Read: See if the result of r & 0000 0000 0000 0000 0000 0000 0000 1000 is 0.

This is very troublesome. For example, we need to write 1 to the third bit of the data at 0x2000 0000. Detailed assembly code:

image-20230613232342659

LDR is to load the following data into the front register, [R1] is to use the value of R1 as an address to obtain the data stored in it.

This is cumbersome, but because of the memory mapping we can directly write and get the data in the "bit alias address".

image-20230613232721037

image-20230613232713587

Bits 0 to 31 at 0x2000 0000 are:

0x2200 0000

0x2200 0004

0x2200 0008

0x2200 000c……

0x2200 007c

So get it directly and modify the data of 0x2200 000c.

0x2000 0000 is mapped to 0x2200 0000 is the sram area mapping, 0x4000 0000 is mapped to 0x4200 0000 is the peripheral area mapping.

The operation is faster, the instruction is less, and it is safer to only access one bit. For example, the 32-bit data of 0x2000 0000 has just been taken out. At this time, the data of 0x2000 0000 is interrupted and modified. At this time, the data we obtained is the old wrong data. Modify Write it back after finishing the third digit, which is equivalent to interrupting the change for nothing.

Program Image

1686670722574

vector: Vector table, which stores information such as the address of the main stack (MSP), the address of the exception, etc.

start-up: The startup code when the board is powered on or rst.

program code: the program code we burned in.

c lib code: library function code.

When reset, first read the msp address to find where the main is. Then read the reset vector to execute the BIOS initialization code, and then start to read the first and second instructions...

1686670972039

Endianness

Two storage specifications.

Such as decimal numbers, 1234, one thousand two hundred and thirty-four. Then we record it in the database, the address is stored as 4321 from low to high, and the bit 1 with the largest weight is stored at the highest address, which is the big endian storage Big endian. Otherwise, the bit with a large weight exists in the low address, and the address of 1234 is from low to high, which means little endian storage. Both methods are supported by m4.

The terms "little endian" and "big endian" come from the book "Gulliver's Trabels" by Jonathan Swift, in which the two warring factions cannot decide which end (little endian) should come from. Still big endian) Open a soft-boiled egg to agree.

Here is Jonathan Swift's description of the history of the big and small endian dispute in 1726:

"...I want to tell you that the two great powers of Lilliput and Blefuscu have been fighting bitterly for the past 36 months. The war started for the following reason: We all believe that before eating eggs, the primitive method is to break eggs. The big end, but the grandfather of the current emperor ate eggs when he was a child, and he happened to break a finger once he cracked eggs according to the ancient method, so his father, the emperor at that time, issued an edict ordering all subjects to break eggs when eating eggs. At the smaller end of the egg, those who violated the order were severely punished. The common people were extremely disgusted with this order. History tells us that there have been six rebellions in this way, in which one emperor died and another lost his throne. Most of these rebellions It was all instigated by the kings and ministers of Blefuscu. After the rebellion subsided, the exiles always fled to that empire to find rescue and refuge. It is estimated that 11,000 people at several times would rather die than break the egg smaller On this dispute, hundreds of major works have been published, but the books of the Daduan faction have always been banned, and the law also stipulates that no one from this faction can be an official.” (This translation is taken from Jiang Jianfeng on the Internet. Translated "Gulliver's Travels" Volume I Chapter 4.)

In his time, Swift was satirizing the ongoing conflict between England (Lilliput) and France (Blefuscu). Danny Cohen, an early pioneer of network protocols, first used these two terms to refer to byte order, and the term has since become widely adopted.

Basic knowledge of big endian and little endian - Zhihu (zhihu.com)

Instruction Set

Instruction Set. The early arm instruction set was 32 bits, with good performance and powerful functions. But too long processing efficiency is low.

The thumb-1 instruction set is 16 bits, the processing efficiency is high, but the performance is also reduced. If the early arm architecture supports two instruction sets, it is necessary to switch modes frequently, which is inefficient.

Later, the thumb-2 instruction set included the early 16-bit and the new 32-bit, and the performance of the mixed instruction set with the arm instruction set did not decrease much, and the code size and processing efficiency were still high.

Assembly

Assembly syntax.

sequential structure

label							; 可省略,用于跳转到此位置
	助记符 operand1, operand2, … ; Comments
	
MOV r1, #0x01					; 数据0x01放入r1
MOV r1, #'A'					; 数据A的ascii码放入r1
MOV R0, R1 						; move R1 into R0
MOVS R0, R1 					; move R1 into R0, 并且更新APSR的状态

LDR R1, [R0]					; R0存的是一个地址值如0x2000 0000, 这个指令是取出R0代表的地址中的数据存入R1
STR R1, [R0]					; 写回去
LDR R0, =0x12345678 			; Set R0 to 0x12345678
; 等效于:
; LDR R0, [PC, #offset] 
; ...
; DCD 0x12345678
; 也就是先在文档末尾的一条指令里写入数据0x12345678,然后编译器自动计算PC+多少offset到达DCD的位置,把其值返给R0
; DCD是声明一个字 32bit,DCB是声明一个Byte
; 如果多个数值的声明可以用标签声明
LDR R3, =MY_NUMBER

ALIGN 4 ; 字要先用这个声明,代表停止长度
MY_NUMBER DCD 0x2000ABCC
HELLO_TEXT DCB “Hello\n”, 0 ; Null terminated string


LDRB R1, [R0]					; B: 只写8位,就是说R0地址处的数据写入R1后,R1高24位清零
SDRH R1, [R0]					; H: 只写16位

LDRSH R1, [R0]					; 视作signed有符号数,写16位

LDRB R0, [R1, #0x3]				; 从R1+3读取一个字节给R0
LDR R3, [R0, R2, LSL #2]		; 从R0+(R2<<2)读取一个字节给R3
LDR R0, [R1], #4				; 赋完值后,令R1=R1+4

ADD R0, R0, R1
ADDS R0, R0, R1					; 加完更新APSR状态,比如有溢出或者进位则更新
ADC R0, R1, R2					; R1+R2还要+APSR的carry位

; SUB SBC类似

MUL R0, R1, R2
UDIV R0, R1, R2
SDIV R0, R1, R2					; signed

Example: It should be signed because it may be reduced to negative

1686672985123

Instructions are 1 word long and half word long. hw1 is used to specify the function, and hw2 is some extensions such as immediate data.

1686713591015

The addresses from low to high are: 4F F0 0A 00 0A 68 10 44...

Every time the PC fetches half a word hw, it jumps to the next hw with +2B.

select structure

	CMP R0, R1						; 相当于if,比较后更新APSR。EQ= LT< GT> LE<= GE >=
	BEQ BRANCH_1					; B是跳转,BL是跳转到函数执行完后返回,BX是根据地址最低位判断目标地址是arm还是thumb在决定跳转到整字还是半字。bx操作数不能是立即数,必须是寄存器
	B BRANCH_2
	
BRANCH_1
	...
	B IFEND							; 不写这个就继续执行BRANCH_2了,像switch的break
BRANCH_2
	...
B IFEND

loop structure

WHILE_BEGIN 
	UDIV R2, R0, R1 ; R2 = n / x
	MUL R3, R2, R1 ; R3 = R2 * x
	CMP R0, R3 ; n == (n / x) * x
	BEQ WHILE_END
	SUBS R1, R1, #1 ; x--
	B WHILE_BEGIN ; loop back
WHILE_END

Stack

There is a data structure in the memory space similar to a stack. The SP pointer points to the top of the stack.

The address of this stack is from high to low, that is, to store data SP–, and to take out data SP++, which is similar to a stack of books turned upside down.

Full stack: The sp pointer points to the last data on the top of the stack.

Empty stack: Points to the next empty location of the last data to put the data in.

In our course, we use an empty stack, which points to the next empty location. To store data, it is stored in SP-4 first, and to fetch data, it is SP+4 first and then popped out of the stack. However, these two instructions do not require us to manually execute, there are special instructions:

PUSH {R0, R4-R7} 	; Push r0, r4, r5, r6, r7
POP {R2-R3, R5} 	; Pop to r2, r3, r5。入栈出栈顺序不是按照书写顺序而是自动根据寄存器地址,高地址值给高地址寄存器

Store 5 data and withdraw 3 data.

Functions

BL first saves the current PC value to LR, then PC jumps to the function address,

BX LR jumps to the address in LR for function return.

Architecture Procedure Call Standard (AAPCS): The specification defines which registers are common to main functions and functions and which are unique.

arm AAPCS stipulates: r0-r3 are general-purpose registers (similar to global variables), but R4-R8, R10-R11 of main and functions are not common (similar to temporary variables, these values ​​will change in the function, not the original function), To be pushed onto the stack to save. The general register values ​​are saved and restored when the function is called and returned. These are performed by the subfunction callee-procedure that calls the original function.

Function call with simple parameters: pass parameters to R0-R3 as function parameters, push R4-R11 onto the stack, and then jump to the function.

1686733381292

Program Memory Use

ROM is read-only data, such as constant constants.

1686733480351

const, static, volatile

It seems that there will not be too many parts involved in the specific code implementation, so I will briefly introduce it first.

const is to define a constant variable, which cannot be modified again after definition.

static usually defines a static function, and the value in the static function is universal, that is, the value of each call to the function is continued from the value of the last call to the function.

volatile: a very important thing in embedded, it has appeared several times in soft exam questions. Presumably it is to prohibit the compiler from optimizing the variable to prevent unnecessary errors.

For example, the compiler optimizes the num variable, so that every time the value of the num variable is modified, it will not be written into the memory immediately. It may first write the modified value into the register, and write it back to the memory when the function returns.

Now, for example, if we are num+=5 in main, the num after the modified value is temporarily stored in the register. Then we call the interrupt, read the current num value from the memory and add 1. But the value in the memory has not been changed, it is still the original value. After returning, main writes the num value in its own hands back to the memory, and finally the num value in the memory is only +5, not +6 as we expected.

Variables declared with volatile will not be optimized in this way. If the value changes, it will be written back to memory immediately. Although it may be inefficient, it is safe.

Interrupts

For example, the logic of our program is to light up a small light when a button is pressed. The first method is polling, keep watching: is the button pressed? No. pressed? without. pressed? ...

This is mainly due to low efficiency and a waste of CPU resources. If the polling interval is large to save resources, it cannot respond in time.

The interrupt allows the CPU to concentrate on processing the background. When the interrupt is triggered, the background is first put down to process the foreground. Simple multi-thread switching can also be realized for bare metal without os.

exception handling process

  1. End the currently executing command.

  2. The current mode register value is pushed onto the stack to save.

    image-20230614171134845

  3. Switch modes.

  4. The PC LR is updated (according to the value provided by the exception handler). The PC checks the interrupt vector table to see where to jump, and assigns EXC_RETURN Code to LR.

  5. Update IPSR status.

  6. Start executing exception code.

  7. Exit, BX LR returns EXC_RETURN Code value to PC.

  8. Pop.

Timing

Interrupting execution is also time-consuming. It takes a certain amount of time to save the state of the source program, execute the interrupt, and recover.

FMax_Int: The maximum interrupt execution frequency, that is, the maximum number of interrupts executed per unit time.

F_CPU: CPU frequency, that is: how many instruction cycles the CPU has per unit time.

C_ISR: How many cycles to execute interrupt content.

C_Overhd: How many cycles are used to interrupt the preparation work such as saving and restoring data.

The cycle required to interrupt an execution: C_Overhd+C_ISR

因此, F M a x _ I n t = F C P U / ( C I S R + C O v e r h d ) F_{Max\_Int=}F_{CPU}/(C_{ISR}+C_{Overhd}) FMax_Int=FCPU/(CISR+COverhd)

U_int: The utilization rate actually consumed by interrupt processing, the above one is the maximum value after all.

U i n t = F I n t / F M a x _ I n t U_{int}=F_{Int}/F_{Max\_Int} Uint=FInt/FMax_Int

Interrupt execution speed (same as frequency): F_Int

Non-interrupt execution speed: (1-U_Int)*F_Int

GPIO

General Purpose Input Output,

Memory-Mapped IO

Map device, control and other registers into memory. The advantage is that the way to access the device is the same as that of the memory, and there is no need to design complex IO circuits, which is convenient; the disadvantage is that it takes up memory space.

Peripheral-Mapped IO

IO has a dedicated storage area, which is different from memory, and there are also special circuit instructions to access IO. The advantage is to save memory space, and you can clearly know when IO occurs; the disadvantage is that the cost of development and design increases.

GPIO

The general-purpose IO can judge the high and low levels of the pins, and can assign high and low levels to the pins for control.

The stm32 has several groups of GPIOs, each with 16 Pins, which can be configured as modes such as input output pullin pullup, as well as functions such as timers, serial ports, and interrupts.

What is the pull-up mode? If it is not set to pull up and down, when the pin is floating (when the input is not set to high or low level), the floating pin may receive electromagnetic wave interference and other problems, resulting in uncertain input status, there are 0 and 1, Easy to make mistakes.

Pull-down: Transistor control defaults to ground, and defaults to low level when there is no input.

Pull-up: Transistor control is connected to Vdd chip working voltage by default.

1686742220470

Most of the pins have these two functions. When we initialize the GPIO, we choose one, and the register controls the corresponding circuit according to the value.

image-20230614193552367

Input and output signals can really be called "signals". The input is defined as 0-0.5 as low level, 0.5-Vdd as high level, and values ​​outside the range are invalid. The output current is only about 5mA, which is incapable of directly driving some devices. We can use some circuits such as triodes, amplifiers, etc., and the circuit receives a signal to know that "the drive current needs to be output" and then output a large current.

Control

Each GPIO port has:

4 * 32bit configuration registers: Configuration related information, such as in/out, pull-up and pull-down, open-drain output or push-pull output, output frequency, etc.

  • Push-pull output push-pull: can output high and low levels.
  • Open-drain output open-drain: no ability to output high level, if you want to output high level, you need to set up a pull-up circuit to output.

2 * 32bit data registers: Input and output data registers.

1 * 32bit set/reset registers: set or reset registers.

1 * 32bit locking registers: Locking registers.

2 * 32bit alternate function selection register.

Mode

As shown in the figure, there are 32 Pins, each with two bits to set 4 modes (in and out optional analog).

1686743308300

Pull

There are only 3 modes (no pull, pull up, pull down).

1686743360055

data

Input and output data registers are separated.

1686743538223

CMSIS

Let me talk about the definition of the test first:

CMSIS transforms memory mapped registers into C structs

#define PORT0 ((struct PORT*)0x2000030)

1686747214412

Let me talk about the understanding discussed with some embedded seniors. The following content is not allowed to be written in the exam:

Teacher Li Ken: A series of APIs and software components launched by arm-M, including core functions, DSP libraries, RTOS support, and debugging interfaces.

Teacher Li Ken: If the chip factory does not want to add another layer, CMSIS is enough; but some manufacturers will seal another layer on it, which may be called the driver layer.

Teacher Li Ken: In addition, CMSIS has a limitation, which is the ARM Cortex-M processor of ARM; although it is very common, not all processors have this core; this needs attention.

Sakaki: This kind of kernel-related files, such as startup files and kernel files, are stipulated by CMSIS.

a6953d9ebb72f2ecd6cc4dbf569d406

Sakaki: Comparing the startup files of STM32F103 and GD32E23, we will find that they are the same:

711dd8daa73cb3dd198e50f32f6f86a

Sakaki: What chip manufacturers need to do is to re-develop library functions according to the interface specified by this arm.

Mr. Li Ken’s C station account: Architect Li Ken’s Blog_CSDN Blog-Programming Life, Blogger in the Field of Fan Welfare

Mr. Sakaki’s C station account: Feng Zhenghao’s blog_CSDN blog-C language, MSP430F5529, Linux domain blogger

Usually Mr. Li Ken's exchange group will discuss many embedded related issues, and interested students are welcome to learn [Doge]

If you are interested in the above content, please take a look.

example:

typedef enum {
    
    
Reset, //!< Resets the pin-mode to the default value.
Input, //!< Sets the pin as an input with no pull-up or pull-down.
Output, //!< Sets the pin as a low impedance output.
PullUp, //!< Enables the internal pull-up resistor and sets as input.
PullDown //!< Enables the internal pull-down resistor and sets as input.
} PinMode;

gpio_set_mode(P1_10, Input);
gpio_set_mode(P2_8, Output);
int PBstatus=gpio_get(P1_10); 
gpio_set(P2_8, 1);

The above code is the driver provided by the teacher. The general idea is to select the pin and pass in specific parameters to set the mode and output.

If you are interested, you can take a look at my article. It is also possible to use the cmsis defined by arm to develop directly:

STM32 study notes_4 GPIO: LED, buzzer, button, sensor usage_Gray sea loose blog-CSDN blog

#include "stm32f10x.h"
int main(void){
    
    
    /* 控制gpio需要三个步骤:开启rcc时钟,初始化,输入输出函数控制 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_SetBits(GPIOA,GPIO_Pin_0);
    while(1){
    
    }
}

The secondary development of drivers can help simplify.

Of course, this paragraph is off topic. The exam is understood as "cmsis is a variable macro definition directly mapped to a register; drivers are to add further behavior to it".

Serial Communication

Serial communication, a communication method for sending messages.

String refers to the way of sending data: one by one serial transmission, parallel may have multiple channels, each channel sends a data at the same time, and multiple channels arrive at the same time.

Serial port communication has simplex Simplex, half duplex Half Duplex, full duplex Full Duplex.

Two transmission methods: Synchronous, which share a clock; Asynchronous, which has its own clock.

Synchronization is very simple. For example, the sender and the receiver both stipulate the falling edge of the clock signal to send and receive.

1686749582568

Asynchronous: It needs to be coordinated through the asynchronous communication protocol Asynchronous Comm. Protocol.

1686749956698

1 start bit flag to start transmission, 7/8/9 data bits, 1 optional parity bit, 1 stop bit.

Both RT parties need to have the same baud rate.

Of course, this is just the simplest serial communication because there are only two parties. If more parties communicate, we need to verify the address to determine which is sent to which; the data requires a more complex verification method.

Asynchronous communication does not require circuits such as synchronous clocks, and the overhead is small, but it is more difficult to develop because of the need for start and end bits.

RS232

Asynchronous communication, Reversed Polarity standard voltage (-3 -15 is 1,3 15 is 0. There are some other standards such as TTL is +5 is 1, -5 is 0.)

There are two types of sending data, ascii code and binary, both of which have to be converted into binary transmission.

uart

For stm32f401.

Full-duplex asynchronous serial port.

In order to process RT buffer data (because it takes time to send and receive data), we can pass the buffer array, the head pointer indicates the position that has been sent, and the tail pointer indicates the end of the data to be sent. Add new data, tail pointer ++; send a data, head pointer ++ until it touches the end.

It turns out that the sender has always sent a high level, and the start frame is a low level frame to indicate that it has started to send data.

How to judge is 1 frame low level? By sampling multiple times in this frame, it is judged whether it is really a low level frame.

Why multiple sampling? Because the two asynchronous signals have a certain offset, multiple sampling is accurate, and it can be determined whether it is really low for the entire frame.

Sampling has a certain sampling rate, not to say that it can really be sampled like an analog signal.

Sampling rate oversampling=16: This is the maximum achievable sampling frequency instead of sampling 16 times per frame.

1686756691253

The receiver first detects the 0 bit for the first time, and starts to suspect: there may be a message on the serial port. This is the first sample of the start frame.

Then check every other frame, 3 5 7 checks 3 times, if both are 0, it means it is indeed possible.

Then continuously check 8910, if there are still two 0s, it means that it is indeed a start frame.

1686756881390

8 sampling rate Because the sampling interval is long, it is easier to encounter high levels on the left and right boundaries, so the error tolerance rate is low. But faster.

calculate

Baud rate calculation:

T x / R x ( b a u d ) = f P C L K 8 ∗ ( 2 − O V E R 8 ) ∗ U S A R T D I V T_x/R_x(baud)=\frac{f_{PCLK}}{8*(2-OVER8)*USARTDIV} Tx/Rx(baud)=8 ( 2 O V ER 8 ) U S A RT D I VfPC L K

OVER8 is the oversampling rate, and fPCLK is the clock frequency.

USARTDIV is a floating point number

How are USARTDIV floating point numbers stored? Convert to hexadecimal by algorithm.

1686759124660

The fractional part is represented by a hexadecimal digit. For example, Example 1 is C, which is 12. After conversion, it is 12/16, which is 0.75.

Example 2 is converted to a hexadecimal system, that is, 0.62*16 is approximately equal to 10, which is A.

The integer part can be directly converted to hexadecimal, 25 in Example 2 is converted to 19, and 27 in Example 1 is converted to 1B.

Then the fractional part of the integer is concatenated (up to 3 integer bits, 1 decimal place, 32-bit register).

Timer

I want the program to run regularly, for example, the led 1s blinks once. How to do it?

The first method is a stupid delay delay. I estimate it myself: well, delay(2000) is about 1s. Then in the program delay, light up, delay, turn off...

What a waste of resources.

The second method, 32, has a timer interrupt.

The general principle of the timer interrupt is that the clock crystal oscillator on the 32 outputs 0101010 at a fixed frequency cycle... There is a cnt in the timer, and ++ is received when a clock crystal oscillator is received.

We can set the timer overflow value, for example, if the overflow value is 1000, adding cnt to 1000 will automatically trigger the timer interrupt. Then return to 0 and continue to ++.

image-20230615014136641

Number of execution cycles: 1+1+1+1+(0xFFFFFFFF-1-1-1 until it becomes 0x00FFFFFF)+(r0+1 execution times, 1 time)

The timer also has some extension methods, for example, we can set ++ or –; we can set the signal source as a clock or an external input square wave signal; we can read the count value...

The common method of our courseware seems to be – to trigger an interrupt at 0, and then restore the initial value.

1686764952191

PWM

What is PWM?

PWM (Pulse Width Modulation) pulse width modulation, in a system with inertia, can obtain the required analog parameters equivalently by modulating the width of a series of pulses, and is often used in fields such as motor speed control.

For example, the speed of your bicycle can only be 100 and 0, and the analog electrical signal can only output high and low.

However, when you ride a bicycle, you have inertia, pedal at 100 speed, pedal at 0 speed, and pedal at 100 speed...

Overall your average bike speed is 50 (we assume acceleration doesn't take time haha)

There are many application scenarios for this, such as setting the LED flicker frequency: high, low, high, low, high, low..., because the frequency is extremely high, we cannot see the flickering with the naked eye, and the visual effect presented to us is to be bright with half the brightness. High low low high low low is 1/3 brightness.

For example, the speed of the motor is adjusted in this way.

So what application scenarios does he have. First, input capture Input capture.

For such an inertial system, we can also read its waveform in reverse to judge its speed. For example, put a speed detection sensor in the motor, and use the input waveform as the clock source signal of the timer. The timer is always ++: record the cnt value when detecting the rising and falling edges, and calculate the time interval by comparing the difference.

image-20230615020829378

Second, output compare output compare.

The timer is always ++, compared with the pre-set threshold, if they are equal, the interrupt output is triggered.

1686767856060

This is PWM. The duty cycle is pretty good.

Low Power Timer

We are currently assuming that the CPU is always running, just switching between background and foreground. There is a low-power timer that causes the CPU to be placed in a low-power state when no timer interrupt occurs, and is only started when a timer interrupt occurs. (use __WFI() wait for instruction instruction)

image-20230621235021294

SysTick

A built-in system clock of the M series uses the processor clock or reference clock as the clock source.

There are four bit registers:

image-20230621235527699

Each assignment is a load, until it reaches 0 and reloads the assignment. ctrl is the control to enable the system clock. This is the clock processing part of the data structure and related operation functions provided by CMSIS.

image-20230621235805628

The init parameter is the number of milliseconds between interrupts. timer_set_callback() is followed by a function that can be defined by itself, so that the function is executed when the timer interrupt is triggered. The above code means that the LED light flips every 100ms, and the CPU is in a low power consumption state under normal conditions.

2C

Transmission scheme for connecting multiple modules: I2C, using two buses.

image-20230622000302459

The two buses are the clock bus SCL and the data bus SDA.

communication process

Now let's go through the process of sending a message from one module (master) to another module (slave) on I2C.

1687363434463

  1. The MCU uses a certain method to identify itself to start transmission.
  2. The MCU sends the address of the LCD slave + a read-write bit, and other modules receive it and find that the address is not their own, so they do not process it.
  3. After LCD receives it, it knows that the target is itself, so it returns ack.
  4. MCU sends a frame of data after receiving ACK.
  5. After sending, the MCU waits for the ACK, and continues to send the next frame of data after receiving the ACK.
  6. Send until the end of sending stop bit stop.

image-20230622000920336

The data length can be set, such as 789.

Devices on the bus communicate half-duplex with open-drain outputs.

image-20230713135505107

The default bus is pulled high by the pull-up resistor.

When the device output out is low, the bus is turned on to ground and the bus is pulled low (the entire bus is pulled low). The example given by Mr. Jiangxie Science and Technology is very good. It is like a crossbar on a bus. Someone pulls the crossbar and pulls it down. The whole crossbar is pulled down. low, someone is using the bus".

Then there is the way the bus transmits data. Under what circumstances do the two buses of SCL and SDA indicate start stop 0 1 bit?

image-20230713140225426

First of all, the value of SDA is only meaningful when SCL is high level.

SDA is from high to low, indicating the start bit. From low to high, indicates the stop bit.

After start bit, SDA high level means 1, low level means 0.

After sending 1byte data, the bus keeps pulling high. If the receiver pulls the bus down and the sender finds that the bus is 1 → 0 (not pulled by the sender himself, the receiver pulled it down for him, but the sender can detect it), it means that the receiver has successfully received and pulled Pull the bus to show "received". If SDA is still at a high level, it means that the receiver has not successfully received or successfully sent ACK.

image-20230713140822834

problem solving

I2C is a very simple master-slave communication protocol, but it has many limitations. For example, the 7-bit address line only allows 2^7 devices; at most two devices communicate master-slave at a time; the speed of a device will affect The communication of the whole bus, etc.

Question 1: What should I do if the processing speed of the slave device is too slow to receive new data frames in the next clock cycle?

Method: clock stretching, pulling down SCL for a period of time to pretend that the next clock cycle has not yet arrived.

image-20230713141906054

Question 2: What should I do if multiple devices send data conflicts at the same time?

Method: Bus Aribitation, we know that the bus is pulled down by a device, and all devices can receive the signal that the bus is pulled down. Therefore, if two devices start sending information at the same time, it doesn’t matter if the previous data is consistent. When the data is inconsistent for the first time, one device sends data 0 and the other sends data 1. At this time, the SDA bus is pulled down by 0 of DATA2.

image-20230713142029471

The device sending DATA1 data will understand: someone is sending data with me at the same time, so the bus is not 1 as I expected, but it is pulled down to 0 by him. Then I quit, you send it. Then there is only the data sent by DATA2.

Question 3: The data sent above is 1byte 8bits every time, which is just right. What if the address to be sent is not 8bits?

Method: less than 8bits are filled with some fixed extra start bits, addresses more than 8bits are filled with two bytes, and those that are not enough are also filled with extra start bits.

image-20230713143052018

Question 3: If my master finishes sending data and wants to receive data immediately and become a slave, is it feasible?

Method: Resend the start bit through an sr signal, that is, repeat start, to identify itself as read instead of write, and restart communication.

image-20230713143601570

addressing format

There are some fixed formats for slave address addressing.

image-20230713143744619

0000 000 0: Broadcast, talk to all slave nodes. If the slave ignores (NACK), it will not participate in the broadcast. Participated if ACK is returned. However, if multiple slaves return ACK, the master does not know who responded.

The second byte sends some behavior related, such as: start, clear, reset software

programming application

slave mode:

  • I2C devices work in slave mode by default.
  • Peripheral clocks are programmed in the I2C_CR2 register. The frequency is between 2kHz~100kHz.
  • The hardware automatically waits for the start and addr information sent.
  • If the addr information is the same as the address stored in OAR1, it means the target is itself. If the ACK bit is 1, send an ack pulse.
  • Set the ADDR bit, 1 means match.
  • If ITEVFEN is the interrupt event flag is 1, an interrupt is generated.
  • The TRA bit indicates whether the slave is in R or T mode (receive or transmit).
  • The BTF bit mark has been confiscated.

1689255491311

image-20230713214410998

It's still a bit confusing to say so. What exactly did I2C go through to send data smoothly?

First, from the concept of the main mode. The master master mode drives the clock signal and initiates the transfer; the slave slave mode responds to the transfer.

main mode

I2C transfer sequence diagram for master sending data

send:

All EV events pull SCL low until the corresponding software sequence is executed.

S: start event. For example, set the peripheral clock in the CR2 register, configure the clock register, rise the clock register, enable CR1 to enable the clock, set the start bit in CR1, wait for the bus to be pulled low to indicate ready, send the start signal, and switch to the main mode.

EV5: Start event successfully, set SB register = 1. Only after SB register = 1 can the address phase be performed, and the SB and EV5 events will be automatically cleared after the address phase is executed.

Address: address stage. Transmit 7-bit address + 1 read-write bit, and then wait for the slave's ack. Receive ack and enter EV6.

EV6: Setting the addr bit=1 means that the address phase is successfully executed, and the master has received the ack. Automatically enter EV8 after clearing EV6.

EV8: Set TxE and prepare to write the data to be sent by the host. TxE indicates that the data register is empty and can be written. Every time data is written to DR, the TxE and EV8 events will be cleared. After writing the data, the data is transmitted, and the host continues to transmit after receiving the ack. Use BTF=1 to indicate the end of data transfer.

void i2c_write(uint8_t address, uint8_t *buffer, int buff_len) {
    
    
	int i = 0;
    // Send in sequence: Start bit, Contents of buffer 0..buff_len, Stop
    while (((I2C1->SR2>>1)&1)); // wait until I2C1 is not busy anymore
    I2C_GenerateSTART(I2C1, ENABLE); // Send I2C1 START condition
    // wait for I2C1 EV5 --> Slave has acknowledged start condition
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
    // Send slave Address for write then wait for EV6
    I2C_Send7bitAddress(I2C1, address, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    while (i < buff_len){
    
    
        I2C_SendData(I2C1, buffer[i]); // send data then wait for EV8_2
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
        i++;
    }
    I2C_GenerateSTOP(I2C1, ENABLE); // send stop bit
}

image-20230714110003657

take over:

The front is the same as the master transmit.

TxE has been changed to RxE, and =1 indicates that data has been received.

After the master sets the stop event (sends NACK), it stops receiving.

void i2c_read(uint8_t address, uint8_t *buffer, int buff_len) {
    
    
    int i = 0;
    // Start bit, Contents of buffer from 0..buff_len, sending a NACK
    // for the last item and an ACK otherwise, Stop bit
    I2C_GenerateSTART(I2C1, ENABLE);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //EV5
    // Send slave Address for write then wait for EV6
    I2C_Send7bitAddress(I2C1, address, I2C_Direction_Receiver);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
    I2C_AcknowledgeConfig(I2C1, ENABLE); // going to send ACK
    while (i < buff_len - 1){
    
    
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); //EV7
        buffer[i] = I2C_ReceiveData(I2C1); // get data byte
        i++;
    }
    I2C_AcknowledgeConfig(I2C1, DISABLE); // going to send NACK
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); //EV7
    buffer[i] = I2C_ReceiveData(I2C1); // get the last byte
    I2C_GenerateSTOP(I2C1, ENABLE); // send stop
}

slave mode

1689259086166

send:

start The start event is initiated by the master. The slave checks the address and decides whether to send an ack bit.

EV1: Setting the addr bit indicates an address match.

EV3-1: Set the TxE bit and start the incoming data. Until the host returns NACK to indicate that it does not want any more data, or AF=1 indicates that the ack fails.

1689259113895

take over:

The front to EV1 and slave transmit are the same.

  1. Data is read from the DR register.
  2. After reading a byte, if the ack bit has been set, return the ack information.
  3. The RxE bit is the status register for received data.
  4. Stop when master generates a stop condition.

abnormal situation:

Bus error, NACK, arbitration failure, clock exception timeout.

image-20230714110916968

Guess you like

Origin blog.csdn.net/jtwqwq/article/details/131719696