One 、 Module hardware learning

1.1. Uart Introduce

Universal asynchronous transmitter receiver transmitter (Universal Asynchronous Receiver/Transmitter), Often referred to as UART, It's an asynchronous transceiver , It's part of the computer hardware . It converts the data to be transmitted between serial communication and parallel communication . As a chip that converts a parallel input signal into a serial output signal ,UART It is usually integrated into the connection of other communication interfaces .

UART It's a universal serial data bus , For asynchronous communication . The bus communicates in both directions , Can achieve full duplex transmission and reception . In embedded devices ,UART Used for communication between host and auxiliary equipment , Such as car sound and external connection AP Communication between , And PC Computer communication includes communication with monitor debugger and other devices , Such as EEPOM signal communication .

1.1.1. Communication protocol

UART As a kind of asynchronous serial communication protocol , The working principle is to transmit each character of data one by one .
The significance of each of you is as follows :

  • Start bit : Let's start with a logic ”0” The signal of , Indicates the beginning of the transmission character .

  • Data bits : Next to the starting bit . The number of data bits can be 5、6、7、8 etc. , Make a character . Usually used ASCII code . Start at the lowest level , Positioning by clock .

  • Parity bit : After adding this bit to the data bit , bring “1” The number of digits should be even ( Even check ) Or odd ( Odd check ), To verify the correctness of data transmission

  • Stop bit : It's the end of a character data . It can be 1 position 、1.5 position 、2 Bit high level .

Because the data is timed on the transmission line , And every device has its own clock , It's likely that there is a small out of sync between the two devices in the communication .
So the stop bit is not just the end of the transmission , And provide the opportunity for the computer to correct the clock synchronization . The more bits apply to stop bits , The greater the tolerance of different clock synchronization , But the data transfer rate is also slower .

  • Free bit : In logic “1” state , Indicates that there is no data transmission on the current line .

Uart The transmission data is shown in the figure 2-1 Shown :

1.1.2. Baud rate

Baud rate is a measure of data transfer rate . Represents the number of symbols transmitted per second (symbol). The amount of information a symbol represents ( Number of bits ) It depends on the order of the sign . For example, transmission uses 256 Order symbols , Every time 8bit Represents a symbol , The data transfer rate is 120 character / second , Then the baud rate is 120 baud, The bit rate is 120*8=960bit/s. It's easy to get the two concepts wrong .

UART The reception and transmission of are carried out according to the same baud rate . The clock frequency generated by baud rate generator is not baud rate clock frequency , It's the baud rate of the clock 16 times , The purpose is to sample accurately at the time of receiving , To extract asynchronous serial data . According to the given crystal clock and the required baud rate , You can calculate the baud rate divider value .

1.1.3. working principle

The process of sending data : Idle state , The line is at high potential ; After receiving the send data command , The time to pull down a bit of data on the line T, Then the data bits are sent from low bit to high bit , After the data is sent , Then the parity check bit and stop bit are sent ( The stop position is high potential ), One frame data transmission ends .

The process of receiving data : Idle state , The line is at high potential ; When the falling edge of the line is detected ( The line potential changes from high to low ) It means that the line has data transmission , Receive data from low bit to high bit according to the agreed baud rate , After receiving the data , Then receive and compare the parity check bits to see if they are correct , If it is correct, it will inform the subsequent device that it is ready to receive data or store it in the cache .

because UART It's asynchronous transmission , There is no transmission synchronization clock . In order to ensure the correctness of data transmission ,UART use 16 Sampling by a clock with data baud rate times . Each data has 16 A clock sample , Take the middle sample value , To ensure that the sampling will not slip or error code . commonly UART The data bits of a frame are 8, So even if every data has a clock error , The receiver can also sample the data correctly .

UART The time sequence of receiving data is : When a data falling edge is detected , Indicates that there is data on the line for transmission , Now the counter CNT Start counting , When counter , When the counter is 8 when , The value of sampling is “0” It means the starting position ; When the counter is 24=161+8 when , The value of sampling is bit0 data ; When the counter value is 40=162+8 when , The value of sampling is bit1 data ; By analogy , Go to the back 6 A sample of data . If parity check bits are needed , If the value of the counter is 152=169+8 when , The sampled values are odd and even bits ; When the counter value is 168=1610+8 when , The value of sampling is “1” Indicates the stop bit , One frame data transmission is completed .

1.1.4. RS232 And RS485

UART: It's usually said UART A serial communication protocol , Specifies the data frame format , Baud rate, etc .
RS232 and RS485: It's two different electrical protocols , in other words , It is the regulation of electrical characteristics and physical characteristics , Acting on the data transmission path , There is no way to deal with it . The corresponding physical devices are RS232 perhaps RS485 Driver chip , take CPU after UART The transmitted voltage signal drives RS232 perhaps RS485 Level logic .

RS232 Use 3-15V Effective level , and UART, Because there are no regulations on electrical characteristics , So use it directly CPU The level used , namely TTL level ( stay 0-3.3V Between ).

More specific , The electrical characteristics also determine the way the lines are connected , such as RS232, It is stipulated that data should be represented by electric level , So the line is single line , Two wires can achieve full duplex ;RS485 Use differential levels to represent data , Therefore, it is necessary to use two wires to achieve the basic requirements of data transmission , To achieve full duplex , You have to use 4 Root line .

RS232 and RS485 The difference between
(1) Anti interference

  • RS485 The interface is a combination of a balanced driver and a differential receiver , Ability to suppress common mode interference , Strong noise immunity .
  • RS232 The interface uses a signal line and a signal return line to form a transmission form for the earth , This common ground transmission is prone to common mode interference , So the noise immunity is weak .
    (2) transmission distance
  • RS485 The standard value of the maximum transmission distance of the interface is 1200 rice (9600bps when ), In fact, it can reach 3000 rice .
  • RS232 The transmission distance is limited , The standard value of maximum transmission distance is 50 rice , In fact, it can only be used 15 Rice or so .
    (3) Communication capability
  • RS485 The interface can be connected on the bus at most 128 A transceiver , That is, it has multi station capability , And such users can take advantage of a single RS485 The interface is convenient to set up the equipment network .
  • RS232 Only one to one communication is allowed .
    (4) Transmission rate
  • RS232 The transmission rate is low , During asynchronous transmission , The baud rate is 20Kbps.
  • RS485 The maximum data transfer rate of is 10Mbps.
    (5) The signal line
  • RS485 full duplex :uart-tx 1 Root line , become RS485- A/B 2 Root line ;uart-rx 1 Root line , become RS485- x/y 2 Root line ,
  • RS485 Half duplex : Will be full duplex A/B; X/Y combined , Time division multiplexing .
  • RS232 Only one to one communication is allowed
    (6) Electrical level value
  • Logic “1” Take the voltage difference between the two lines as +(2-6)V Express ; Logic “0” Take the voltage difference between the two lines as -(2-6)V Express .
  • stay RS232 The voltage of any signal in is a negative logic relationship . namely : Logic “1”-5-15V; Logic “0”,+5~+15V, The noise tolerance is 2V. That is to say, the receiver should be able to recognize as low as +3V As logic “0”, Up to -3V The signal of the signal as logic “1”.
  • RS232 The signal level of the interface is high , A chip that can easily damage the interface circuit , And because it's related to TTL The level is not compatible, so the level conversion circuit can be used TTL Circuit connection .
  • RS485 Interface signal level ratio RS232 To reduce the , It's not easy to damage the chip of the interface circuit , And this level is related to TTL Level compatible , Convenience and TTL Circuit connection .

1.1.5. Flow control

When data is transmitted through two serial ports , Data loss often occurs , Or the processing speed of the two computers is different , Such as the communication between desktop computer and MCU , The receiving data buffer is full , At this point, the data that continues to be sent will be lost , Flow control can solve this problem , When the receiving end can't process the data , Just send out “ No longer receive ” The signal of , The sender stops sending , Until receipt “ You can continue sending ” And then send the data .

So flow control can control the process of data transmission , Prevent data loss .PC The two kinds of flow control commonly used in the machine are : Hardware flow control ( Include RTS/CTS、DTR/CTS etc. ) And software flow control XON/XOFF( continue / stop it ). Hardware flow control

Hardware flow control commonly used are RTS/CTS Flow control and DTR/DSR There are two kinds of flow control .
DTR– Data terminal ready (Data Terminal Ready)
Low efficiency , When it's low , Indicates that the device itself is ready . This signal is output to the opposite end device , Use the peer device to decide whether to communicate with the device .
DSR- The data device is ready (Data Set Ready)
Low efficiency , This signal is provided by the end-to-end equipment connected to this equipment , When it's low , This device can communicate with the device .
RTS - Request to send ( data )(Request To Send)
Low efficiency , This signal is set by the device when it needs to send data to the opposite device . When it's low , Indicates that the device has data to send to the opposite device . Whether the end-to-end device can receive the data sent by itself , Through CTS Signal to answer .
CTS - Receive and send ( request )(Clear To Send)
Low efficiency , Whether the peer device can receive the data sent by itself , from CTS decision . if CTS For low , It means the opposite end is ready , It can receive the data sent by the local end .

With RTS/CTS Flow control analysis , The analysis host sends / Receiving process :

The physical connection

The host RTS( The output signal ), Connected to the slave CTS( Input signal ).
The main engine is CTS( Input signal ), Connected to the slave RTS( Input signal ).

  • 1. The sending process of the host :
    The host queries the host's CTS Foot signals , This signal is connected to RTS The signal , Controlled by slave machine . If the host CTS Signal valid ( For low ), Indicates the reception of the slave FIFO under , The slave can receive , At this time, the host can send data to the slave , And in the process of sending to always query CTS Whether the signal is valid . The host found CTS invalid , Then stop sending .
    The host CTS When will the signal fail ?
    When the slave receives the data sent by the host , The receiving module of the slave is FIFO If the full , It will make the slave RTS Invalid , That is, the host's CTS Invalid signal . The host found CTS invalid , Host send abort .

  • 2. Host receiving mode :
    If the host receives FIFO under , So make the host RTS Signal valid ( For low ), That is to say, the slave's CTS Signal valid . At this time, if the slave wants to send , Before sending, it will query the slave's CTS The signal , If effective , Start sending . And always query the slave during the sending process CTS The valid state of the signal , If it is invalid, the transmission is terminated . Whether it is valid or not depends on RTS The signal decides . If the host FIFO Full of , Then the host's RTS Invalid signal , That is, the slave machine CTS Invalid signal , Host reception aborted . Software flow control

Due to the limitation of cable , In general, hardware flow control is not used in control communication , It's using software flow control . Usually by XON/XOFF To achieve software flow control . The common method is : When the amount of data in the input buffer of the receiver exceeds the set high bit , Send it to the data sender XOFF Stop sending the data immediately after the character ; When the amount of data in the input buffer of the receiver is lower than the set low bit , Send it to the data sender XON character ( Decimal 17 or Control-Q), The sender receives XON Start sending data as soon as the character is written . Generally, the sender can be found in the source program of the equipment XON Send the data immediately after the character . Generally, you can find what bytes are sent from the source program of the device .

Attention should be paid to , If the transmission is binary data , Flag characters may also appear in the data stream and cause misoperation , This is a flaw in software flow control , And hardware flow control will not have such a problem .

Two 、Linux serial frame

stay Linux In the system , The terminal is a character type device , There are many types of it , Usually use tty(Teletype) For short, various types of terminal equipment . For embedded systems , The most commonly used is Uart(Universal Asynchronous Receiver/Transmitter), Serial port , It's called port in our daily life

2.1. TTY Driver framework

2.1.1. TTY Concept Serial terminal (/dev/ttyS*)

The terminal uses serial port to connect the computer .Linux Think of each serial port as a character device . The device names corresponding to these serial ports are /dev/ttySAC*; Console terminal (/dev/console)

stay Linux In the system , The output device of a computer is often called a console terminal , This is especially for printk Information output to the device ./dev/console It's a virtual device , It needs to map to the real tty On , For example, through the kernel boot parameters “console=ttySCA0” Just put console Mapped to the serial port 0 Virtual terminal (/dev/tty*)

When the user logs in , Using a virtual terminal . Use Ctcl+Alt[F1 - F6] When you combine keys , We can switch to tty1、tty2、tty3 Wait up there .tty* It's called a virtual terminal , and tty0 Is an alias of the virtual terminal currently used .

2.1.2. TTY Architecture analysis

Whole tty The architecture looks like this 3.1 Shown , It can be divided into two levels , The first layer is our serial driver layer , It's in direct contact with the hardware , We need to fill in a struct uart_ops The structure of the body , The other layer is the upper layer tty layer , Include tty Core and line protocol , Each of them has one Ops structure , The user space is tty Registered character device node to access .

 chart 3.1tty Architecture diagram

Pictured 3.2 Shown ,tty The process of sending data by the device is :tty The core is obtained from a user and will be sent to a tty Device data ,tty The core passes the data to tty Line regulation drives , And then the data goes to tty drive ,tty The driver converts the data into a format that can be sent to the hardware .

The process of receiving data is : from tty The data received by the hardware goes up to tty drive , Then enter the tty Line regulation drives , Enter again tty The core , Here it's captured by a user .

 chart 3.2 tty Device send 、 Receiving data flow

2.2. Key data structure

2.2.1. Struct uart_driver

uart_driver Contains the serial device name , Serial driver name , Primary and secondary equipment number , Serial console ( Optional )) Etc , It also encapsulates tty_driver
( The underlying serial driver doesn't need to care tty_driver)

struct uart_driver {struct module    *owner; /* Have the right to uart_driver Module , It's usually THIS_MODULE*/const char        *driver_name; /* Driver serial port name , Serial device name is based on driver name */const char        *dev_name; /* Serial device name */int                 major; /* The main equipment, */int                 minor; /* Secondary device number */int                 nr; /* The uart_driver Number of serial ports supported */struct console    *cons; /* Their corresponding console, If so uart_driver Support serial console,
* Otherwise NULL*//*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/struct uart_state *state; /* The lower , Window driver layer */struct tty_driver  *tty_driver; /*tty relevant */

2.2.2. struct console

The structure that must be registered to realize the printing function of the console

struct console {  char name[16];  void(*write)(struct console *,const char *, unsigined);  int (*read)(struct console *, char *, unsigned);  struct tty_driver *(struct console *,int*);  void (*unblank)(void);  int  (*setup)(struct console *, char *);  int  (*early_setup)(void);  short  flags;  short  index; /* To specify the console Which one to use uart port ( Corresponding uart_port Medium line), If -1,kernel The first one will be selected automatically uart port*/  int   cflag;  void  *data;  struct   console *next;};

2.2.3. struct uart_state

every last uart Port corresponds to a uart_state, The structure will uart_port With the corresponding circ_buf Connect .uart_state There are two members that will be used in the underlying serial driver :xmit and port. When user space program sends data through serial port , The upper driver saves the user data in the xmit; The serial port sends interrupt processing function through xmit Get user data and send it out . Serial port receive interrupt processing function needs to pass port Transmit the received data to the line protocol layer .

struct uart_state {   struct  tty_port  port;   
       enum uart_pm_state   pm_state;   struct circ_buf     xmit;   
       struct uart_port     *uart_port; /* Corresponding to a serial device */};

2.2.4. struct uart_port

uart_port Used to describe the serial port I/O Port or I/O Memory address 、FIFO size 、 Port type 、 Serial clock and other information . actually , One uart_port The implementation corresponds to a serial port device .

struct uart_port {spinlock_t              lock;                   /* port lock */unsigned long           iobase;                 /* in/out[bwl] */unsigned char __iomem   *membase;               /* read/write[bwl] */unsigned int            (*serial_in)(struct uart_port *, int);void                    (*serial_out)(struct uart_port *, int, int);void                    (*set_termios)(struct uart_port *,   struct ktermios *new,   struct ktermios *old);int                     (*handle_irq)(struct uart_port *);void                    (*pm)(struct uart_port *, unsigned int state,  unsigned int old);void                    (*handle_break)(struct uart_port *);unsigned int            irq;                    /* irq number */unsigned long           irqflags;               /* irq flags  */unsigned int            uartclk;                /* base uart clock */unsigned int            fifosize;               /* tx fifo size */unsigned char           x_char;                 /* xon/xoff char */unsigned char           regshift;               /* reg offset shift */unsigned char           iotype;                 /* io access style */unsigned char           unused1;#define UPIO_PORT               (0)#define UPIO_HUB6               (1)#define UPIO_MEM                (2)#define UPIO_MEM32              (3)#define UPIO_AU                 (4)                     /* Au1x00 and RT288x type IO */#define UPIO_TSI                (5)                     /* Tsi108/109 type IO */unsigned int            read_status_mask;       /* driver specific */unsigned int            ignore_status_mask;     /* driver specific */struct uart_state       *state;                 /* pointer to parent state */struct uart_icount      icount;                 /* statistics */struct console          *cons;                  /* struct console, if any */#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)unsigned long           sysrq;                  /* sysrq timeout */#endifupf_t                   flags;#define UPF_FOURPORT            ((__force upf_t) (1 << 1))#define UPF_SAK                 ((__force upf_t) (1 << 2))#define UPF_SPD_MASK            ((__force upf_t) (0x1030))#define UPF_SPD_HI              ((__force upf_t) (0x0010))#define UPF_SPD_VHI             ((__force upf_t) (0x0020))#define UPF_SPD_CUST            ((__force upf_t) (0x0030))#define UPF_SPD_SHI             ((__force upf_t) (0x1000))#define UPF_SPD_WARP            ((__force upf_t) (0x1010))#define UPF_SKIP_TEST           ((__force upf_t) (1 << 6))#define UPF_AUTO_IRQ            ((__force upf_t) (1 << 7))#define UPF_HARDPPS_CD          ((__force upf_t) (1 << 11))#define UPF_LOW_LATENCY         ((__force upf_t) (1 << 13))#define UPF_BUGGY_UART          ((__force upf_t) (1 << 14))#define UPF_NO_TXEN_TEST        ((__force upf_t) (1 << 15))#define UPF_MAGIC_MULTIPLIER    ((__force upf_t) (1 << 16))/* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */#define UPF_HARD_FLOW           ((__force upf_t) (1 << 21))/* Port has hardware-assisted s/w flow control */#define UPF_SOFT_FLOW           ((__force upf_t) (1 << 22))#define UPF_CONS_FLOW           ((__force upf_t) (1 << 23))#define UPF_SHARE_IRQ           ((__force upf_t) (1 << 24))#define UPF_EXAR_EFR            ((__force upf_t) (1 << 25))#define UPF_BUG_THRE            ((__force upf_t) (1 << 26))/* The exact UART type is known and should not be probed.  */#define UPF_FIXED_TYPE          ((__force upf_t) (1 << 27))#define UPF_BOOT_AUTOCONF       ((__force upf_t) (1 << 28))#define UPF_FIXED_PORT          ((__force upf_t) (1 << 29))#define UPF_DEAD                ((__force upf_t) (1 << 30))#define UPF_IOREMAP             ((__force upf_t) (1 << 31))#define UPF_CHANGE_MASK         ((__force upf_t) (0x17fff))#define UPF_USR_MASK            ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))unsigned int            mctrl;                  /* current modem ctrl settings */unsigned int            timeout;                /* character-based timeout */unsigned int            type;                   /* port type */const struct uart_ops   *ops;unsigned int            custom_divisor;unsigned int            line;                   /* port index */resource_size_t         mapbase;                /* for ioremap */struct device           *dev;                   /* parent device */unsigned char           hub6;                   /* this should be in the 8250 driver */unsigned char           suspended;unsigned char           irq_wake;unsigned char           unused[2];void                    *private_data;          /* generic platform data pointer */};

2.2.5. struct uart_ops

struct uart_ops It covers all the operations of the driver to the serial port

 struct uart_ops {unsigned int    (*tx_empty)(struct uart_port *);void            (*set_mctrl)(struct uart_port *, unsigned int mctrl);unsigned int    (*get_mctrl)(struct uart_port *);void            (*stop_tx)(struct uart_port *);void            (*start_tx)(struct uart_port *);void            (*throttle)(struct uart_port *);void            (*unthrottle)(struct uart_port *);void            (*send_xchar)(struct uart_port *, char ch);void            (*stop_rx)(struct uart_port *);void            (*enable_ms)(struct uart_port *);void            (*break_ctl)(struct uart_port *, int ctl);int             (*startup)(struct uart_port *);void            (*shutdown)(struct uart_port *);void            (*flush_buffer)(struct uart_port *);void            (*set_termios)(struct uart_port *, struct ktermios *new,   struct ktermios *old);void            (*set_ldisc)(struct uart_port *, int new);void            (*pm)(struct uart_port *, unsigned int state,  unsigned int oldstate);int             (*set_wake)(struct uart_port *, unsigned int state);/*
         * Return a string describing the type of the port
         */const char      *(*type)(struct uart_port *);/*
         * Release IO and memory resources used by the port.
         * This includes iounmap if necessary.
         */void            (*release_port)(struct uart_port *);/*
         * Request IO and memory resources used by the port.
         * This includes iomapping the port if necessary.
         */int             (*request_port)(struct uart_port *);void            (*config_port)(struct uart_port *, int);int             (*verify_port)(struct uart_port *, struct serial_struct *);int             (*ioctl)(struct uart_port *, unsigned int, unsigned long);#ifdef CONFIG_CONSOLE_POLLint             (*poll_init)(struct uart_port *);void            (*poll_put_char)(struct uart_port *, unsigned char);int             (*poll_get_char)(struct uart_port *);#endif};

2.3. The key process

2.3.1. Registration process register uart_driver

This interface is in uart driver Call in , Used to register uart_driver To kernel in , The call phase is in uart driver The initial stage of , for example :module_init(), uart_driver The registration flow chart of

 chart 3.3uart driver Registration process

The registration process mainly does the following operations :

  • 1、 according to driver Maximum number of devices supported , apply n individual uart_state Space , every last uart_state There is one. uart_port.
  • 2、 Allocate one tty_driver, And will uart_driver->tty_driver Pointing to it .
  • 3、 Yes tty_driver Set it up , This includes the default baud rate 、 Inspection methods, etc , There is also an important ops, Structure tty_operation Registration of , It is tty The interface between core and serial driver .
  • 4、 Initialize each uart_state Of tty_port;
  • 5、 register tty_driver.
    register uart_driver It's actually registration tty_driver, Dealing with user space is entirely up to tty_driver, This part is implemented by the kernel and does not need to be modified add to uart_port

This interface is used to register a uart port To uart driver On , By registering ,uart driver You can access the corresponding uart port, Send and receive data . The interface is in uart driver Medium probe Function call , It must be ensured that it is later than uart_register_drver The registration process .

uart driver Before calling the interface , To set it manually uart_port The operation of uart_ops, By calling uart_add_one_port After the interface driver completes the hardware operation interface registration .uart add to port Flow chart 3-4 Shown :

 chart 3-4 uart add to port flow chart

2.4. Data sending and receiving process

2.4.1. Turn on the device (open operation )

open The general flow of the equipment is shown in the figure 3-5 Shown :

 chart 3-5 open Equipment process

2.4.2. Data transmission process (write operation )

The general flow of sending data is shown in the figure 3-6 Shown :

 chart 3-6 Send data flow

2.4.3. Data receiving process (read operation )

The general flow of receiving data is shown in the figure 3-7 Shown :

 chart 3-7 Data receiving process

2.4.4. Turn off the device (close operation )

close The general flow of the equipment is shown in the figure 3-8 Shown :

 chart 3-8 close Equipment process

2.4.5. Cancellation process remove uart_port

This interface is used from uart driver Last one uart port, The interface is in uart driver Medium remove Call in function .uart remove port The flow chart of 3-9 Shown :

 chart 3.9 uart remove port flow chart Cancellation uart_driver

This interface is in uart driver Call in , Used to from kernel The cancellation uart_driver, The call phase is in uart driver The exit phase of , for example :module_exit(),uart driver The cancellation process is shown in the figure 3.10 Shown

2.5. Use rs485 signal communication

2.5.1. rs485 and rs232 The difference between

uart(TTL-3.3V)/rs232( Industrial grade ±12V) It's voltage driven ,rs485 It's current driven ( It's capable of transmitting over longer distances )
rS232 Use the level to represent the data , Use 2 The root line can achieve full duplex ,rs485 Use differential level to represent data , So we have to use 4 The root line is full duplex rs485;

full duplex :uart-tx 1 The root line becomes rs485-A/B 2 Root line ;uart-rx 1 The root line becomes rs485- X/Y Two wires ;

rs485 Half duplex : Will be full duplex A/B and X/Y Combined, time-sharing multiplexing ;
rs485-de/re It's a control signal to the converter , For our chips , It's all output ;

2.5.2. rs485 Debugging method :

First of all to ensure uart Modules and related gpio, The voltage conversion chip is working properly :

  • a, Guarantee uart tx/rx Function is normal .
  • b, use gpio-output To control de/re dependent 2 individual gpio, Observe de/re Of gpio Output low/high If there is something wrong
  • c, stay b On the basis of , Debug separately rs485-tx/rs485-rx, Is single ended debugging pass.

Pattern 1
(2 individual gpio Independent control de/re, enable It's about relating gpio Set to active level ; no need uart Controller rs485 Pattern ;uart The controller is in normal Pattern )

  • a, Default re-en, de-dis, Default rs485-rx
  • b, When it comes to sending ,re-dis, de-enable, then uart-tx.
  • c, tx When it's done ,de-dis; re-en, Enter the default rs485-rx Pattern .

Pattern 2
The premise of this model , Peripheral devices de/re It has to be of opposite polarity , such as de It's high level effective ,re It's low level effective , You can use one gpio, To control de/re, here de/re It must be mutually exclusive .
(1 individual gpio control de/re, enable It's about relating gpio Set to active level ; no need uart Controller rs485 Pattern ;uart The controller is in normal Pattern )

  • a, re-en, Get into rs485-rx Pattern (re Usually the low level is effective , This step is Set up re Corresponding gpio Low level )
  • b, When it comes to sending , Set up gpio:re-disable, de-enable, then uart-tx.(re Usually the low level is effective , This step is Set up re Corresponding gpio High level )
  • c, tx When it's done ,de-disable; re-enable, Enter the default rs485-rx Pattern .(re Usually the low level is effective , This step is Set up re Corresponding gpio Low level )

Pattern 3
rs485-software-halfduplex(de/re Independent output )
( Can make uart Controller rs485 Pattern ; adopt uart Inside the module reg To control de/re The signal )

  • a, Can make uart Controller rs485 Pattern , And according to the characteristics of the voltage conversion chip , Set up de/re polarity
  • b, Set up rs485 Model for sw-half-duplex, Set up de-timing register ; Set up de/re turnaround register .
  • c, The default is rs485-rx Pattern , Set up de-dis/re-en
  • d, When you want tx When , Set up de-en/re-dis
  • e, Send complete , Set up de-dis/re-en

Pattern 4
rs485-hardware-halfduplex(de/re Independent output )
The basic configuration is in the same mode 3, But set up rs485 The model is hardware-halfduplex Pattern

  • a, Just set de-en/rx-en All for 1, And then you don't have to worry , Hardware implementation of half duplex switching .

Pattern 5:
Using pure hardware to achieve RS485 Half duplex function , The circuit is shown in the figure :

receive :
When there is no data by default ,UART_TX High level , Triode conduction ,485 chip RE Low level enable ,RO Receive data enable , At that moment, from 485AB What data you receive will pass through RO Channel to MCU, Complete the data receiving process .
send out :
When sending data ,UART_TX There will be a pull-down level , Start sending data , Now the triode is off ,DE Enable for high level transmission . When sending data ‘0’ when , because DI Mouth to mouth , Now the data ‘0’ It will be transmitted to AB mouth A-B<0, transmission ‘0’, Complete the low-level transmission . When sending ‘1’ when , Now the triode is on , Logically speaking RO Can make , At this time, because it is still in the process of sending data , In this state 485 In a high resistance state , The state at this point is through A Pull up B Pull down resistance determines , here A-B>0 transmission ‘1’, Complete high level transmission .

3. Module detailed design

3.1. Key function interfaces

3.1.1. uart_register_driver

/* function :  uart_register_driver For serial driver uart_driver Register to kernel ( Serial core layer ) in , This function is usually called in the module initialization function .
 * Parameters :drv: To register the uart_driver
 * Return value : success , return 0; Otherwise, an error code is returned
 */int uart_register_driver(struct uart_driver *drv)

3.1.2. uart_unregister_driver

/* function :uart_unregister  To cancel our registered uart_driver, This function is usually called in the module unload function ,
 * Parameters  : drv: To log off uart_driver
 * Return value : Successfully returns 0, Otherwise, an error code is returned
 */void uart_unregister_driver(struct uart_driver *drv)

3.1.3. uart_add_one_port

/* function :uart_add_one_port Used to add a serial port for the serial driver , Usually after detecting the device ( Driven devices probe Method ) Call this function
 * Parameters :
 *     drv: Serial driver
 *     port: Serial port to add
 * Return value : success , return 0; Otherwise, an error code is returned
 */int uart_add_one_port(struct uart_driver *drv,struct uart_port *port)

3.1.4. uart_remove_one_port

/* function :uart_remove_one_port Used to delete a serial port that has been added to the serial driver , This function is usually called when the driver is unloaded
 * Parameters :
 *     drv: Serial driver
 *     port: Serial port to delete
 * Return value : success , return 0; Otherwise, an error code is returned
 */int uart_remove_one_port(struct uart_driver *drv,struct uart_port *port)

3.1.5. uart_write_wakeup

/* function :uart_write_wakeup Wake up the process blocked by serial port data writing in the upper layer , This function is usually invoked in the interrupt handling function of serial port.
 * Parameters :
 *     port:  Need to wake up the serial port of the write blocking process
 */void uart_write_wakeup(struct uart_port *port)

3.1.6. uart_suspend_port

/* function :uart_suspend_port Used to suspend a specific serial port
 * Parameters :
 *     drv: The serial port driver of the serial port lock to be suspended
 *     port: Serial port to suspend
 * Return value : Successfully returns 0; Otherwise, an error code is returned
 */int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)

3.1.7. uart_resume_port

/* function :uart_resume_port Used to recover a suspended serial port
 * Parameters :
 *     drv: The serial port driver of the serial port to be recovered
 *     port: Serial port to recover
 * Return value : Successfully returns 0; Otherwise, an error code is returned
 */int uart_resume_port(struct uart_driver *drv, struct uart_port *port)

3.1.8. uart_get_baud_rate

/* function :uart_get_baud_rate Through the decoding termios Structure to get the baud rate of the specified serial port
 * Parameters :
 *     port: To get the baud rate of the serial port
 *     termios: The current expectation is termios To configure ( Including serial port baud rate )
 *     old: Former termios To configure , It can be for NULL
 *     min: Minimum acceptable baud rate
 *     max: The maximum acceptable baud rate
 *      Return value : Serial port baud rate
 */unsigned int uart_get_baund_rate(struct uart_port *port, struct ktermios *termios, struct ktermios *old,unsigned int min, unsigned int max)

3.1.9. uart_get_divisor

/* function :uart_get_divisor  It is used to calculate the frequency division number of serial port clock at a certain wave rate ( Serial port baud rate divisor )
 * Parameters :
 *     port: The serial port to calculate the frequency division number
 *     baud: Expected baud rate
 * Return value : Serial clock frequency division number
 */unsigned int uart_get_divisor(struct uart_port *port, unsigned int baund)

3.1.10. uart_update_timeout

/* function :uart_update_timeout Used to update the ( Set up ) A serial port FIFO Out of time
 * Parameters :
 *     port: To update the out of time serial port
 *     cfalg:termios Structure of the cflag value
 *     baud: Baud rate of serial port
 */void uart_update_timeout(struct uart_port *port,unsigned int cflag, unsigned int baud)

3.1.11. uart_insert_char

/* function :uart_insert_char Used to direct to uart Layer inserts a character
 * Parameters :
 *     port: Serial port to write information
 *     status:RX buffer state
 *     overrun: stay status Medium overrun bit Mask
 *     ch: The characters that need to be inserted
 *     flag: Insert the character of flag:TTY_BREAK,TTY_PSRIYY, TTY_FRAME
 */void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun,unsigned int ch, unsigned int flag)

3.1.12. uart_console_write

/* function :uart_console_write Used to write a console message to the serial port
 * Parameters :
 *     port: Serial port to write information
 *     s: Information to write
 *     count: The size of the information
 *     putchar: Function used to write characters to the serial port , This function takes two arguments : Serial port and characters to write
 */Void uart_console_write(struct uart_port *port,const char *s, unsigned int count,viod(*putchar)(struct uart_port*, int))

4. Module instruction

4.1. A serial port programming,

4.1.1. Serial control function

attribute explain
tcgetatrr Take property (termios structure )
tcsetarr Set properties (termios structure )
cfgetispeed Get the input speed
cfsetispeed Get the output speed
cfstospeed Set the output speed
tcdrain Wait for all outputs to be transmitted
tcflow Suspend transmission or reception
tcflush Brush please pending output and / Or input
tcsendbreak send BREAK character
tcgetpgrp Get the foreground process group ID
Tcsetpgrp Set the foreground process group ID

4.1.2. Serial port configuration process

  • (1) Keep the original serial port configuration , Use tegetatrr(fd, &oldtio);
struct termious newtio, oldtio;tegetattr(fd, &oldtio);

  • (2) The activation options are CLOCAL and CREAD, For local connection and reception use
newtio.cflag |= CLOCAL|CREAD;

  • (3) set baud rate
newtio.c_cflag = B115200;

  • (4) set data bit , You need to use mask settings
newtio.c_cflag &= ~CSIZE;Newtio.c_cflag |= CS8;

  • (5) Set stop bit , By activating c_cflag Medium CSTOP Realization . If the stop bit is 1, Then remove CSTOPB, If the stop bit is 2, Then activate CSTOP
newtio.c_cflag &= ~CSTOPB; /* The stop bit is set to 1*/Newtio.c_cflag |= CSTOPB; /* The stop bit is set to 2 */

  • (6) Set up flow control
newtio.c_cfag |= CRTSCTS /* Turn on hardware flow control  */newtio.c_cfag |= (IXON | IXOFF | IXANY); /* Turn on software flow control */

  • (7) Parity bit settings , Use c_cflag and c_ifag.
    Set odd parity
newtio.c_cflag |= PARENB;newtio.c_cflag |= PARODD;newtio.c_iflag |= (INPCK | ISTRIP);

Set even parity

newtio.c_iflag |= (INPCK | ISTRIP);newtio.c_cflag |= PARENB;newtio.c_cflag |= ~PARODD;

  • (8) Set minimum characters and wait time , There are no special requirements for receiving characters and waiting time , Can be set to 0:
newtio.c_cc[VTIME] = 0;newtio.c_cc[VMIN]  = 0;

  • (9) Process reference object to write
    tcflush Function cleaning ( abandon ) Input buffer ( The terminal program has received , But the user program has not yet read ) Or output buffer ( The user program has written , But not sent ).
int tcflash(int filedes, int quene)quene The number should be one of the following three constants :
  *TCIFLUSH  Clear the input queue   *TCOFLUSH  Clear the output queue   *TCIOFLUSH  Swipe input 、 Output queue
for example :tcflush(fd, TCIFLUSH);

  • (10) Activate configuration , After the configuration is complete , The configuration needs to be activated to take effect . Use tcsetattr() function :
int tcsetarr(int filedes, const struct termios *termptr);opt  Specify when the new terminal property will work ,   *TCSANOW: Changes happen immediately    *TCSADRAIN: The change doesn't happen until all the output is sent . If you change the output parameters, you should use this option    *TCSAFLUSH: The change doesn't happen until all the output is sent . Further more , Unread at the time of the change
                  All input data is deleted ( clear of a charge )
for example :tcsetatrr(fd, TCSANOW, &newtio);

4.1.3. Usage flow

  • (1) Open the serial port , for example "/dev/ttySLB0"
fd = open("/dev/ttySLB0",O_RDWR | O_NOCTTY | O_NDELAY);O_NOCTTY: It's to tell Linux This program won't be on this port “ Control terminal ”. If not , All the input , Like from the keyboard Ctrl+C Stop signals and so on , It will affect your progress .
O_NDELAY: The sign is to tell Linux This program doesn't care about DCD Status of signal line , That is, no matter whether there is data coming from the serial port , It's all non blocking , Program continues .

  • (2) Restore the serial port status to blocking status , Used to wait for serial data to be read in , use fcntl function :
fcntl(fd,F_SETFL,0);  //F_SETFL: Settings file flag by 0, Default , Blocking state 

  • (3) Then test whether the open file descriptor is applied to a terminal device , To further confirm whether the serial port is opened correctly .

  • (4) Read write serial port
 The reading and writing of serial port is the same as that of ordinary file , Use read,write function read(fd, buf ,8);write(fd,buff,8);

4.1.4. Demo

The following is an example of data collection by temperature measurement module

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <termios.h>#include <stdio.h>#include <string.h>#include <unistd.h>#include <log/log.h>#include <stdlib.h>#define UART_DEVICE     "/dev/ttySLB1"struct temp {float temp_max1;float temp_max2;float temp_max3;float temp_min;float temp_mean;float temp_enviromem;char temp_col[1536];};int main(void){int count, i, fd;struct termios oldtio, newtio;struct temp *temp;
temp = (struct temp *)malloc(sizeof(struct temp));if (!temp) {printf("malloc failed\n");return -1;}char cmd_buf1[] = { 0xAA, 0x01, 0x04, 0x00, 0x06, 0x10, 0x05, 0x00, 0xBB};char cmd_buf2[] = { 0xAA, 0x01, 0x04, 0x00, 0x00, 0xA0, 0x00, 0x03, 0xBB};char cmd_buf3[] = { 0xAA, 0x01, 0x04, 0x00, 0x03, 0x10, 0x01, 0x00, 0xBB};char read_buf[2000];//----------- open uart Device file ------------------
fd = open(UART_DEVICE, O_RDWR | O_NOCTTY);if (fd < 0) {printf("Open %s failed\n", UART_DEVICE);return -1;} else {printf("Open %s successfully\n", UART_DEVICE);}//----------- Set operating parameters -----------------------tcgetattr(fd, &oldtio);// Get the current operation mode parameters memset(&newtio, 0, sizeof(newtio));// Baud rate =230400  Data bits =8  Enable data reception
newtio.c_cflag = B230400 | CS8 | CLOCAL | CREAD | CSTOPB;
newtio.c_iflag = IGNPAR;tcflush(fd, TCIFLUSH);// Clear the input and output buffers tcsetattr(fd, TCSANOW, &newtio);// Set new operation parameters //printf("input: %s, len = %d\n", cmd_buf, strlen(cmd_buf));//------------ towards urat send data -------------------for (i = 0; i < 9; i++)printf("%#X ", cmd_buf1[i]);
count = write(fd, cmd_buf1, 9);if (count != 9) {printf("send failed\n");return -1;}usleep(500000);memset(read_buf, 0, sizeof(read_buf));
count = read(fd, read_buf, sizeof(read_buf));if (count > 0) {for (i = 0; i < count; i++);
temp->temp_max1 = read_buf[7] << 8 | read_buf[6];
temp->temp_max2 = read_buf[9] << 8 | read_buf[8];
temp->temp_max3 = read_buf[11] << 8 | read_buf[10];
temp->temp_min  = read_buf[13] << 8 | read_buf[12];
temp->temp_mean = read_buf[15] << 8 | read_buf[14];printf("temp->temp_max1 = %f\n", temp->temp_max1 * 0.01);printf("temp->temp_max2 = %f\n", temp->temp_max2 * 0.01);printf("temp->temp_max3 = %f\n", temp->temp_max3 * 0.01);printf("temp->temp_min  = %f\n", temp->temp_min  * 0.01);printf("temp->temp_mean = %f\n", temp->temp_mean * 0.01);} else {printf("read temp failed\n");return -1;}
count = write(fd, cmd_buf3, 9);if (count != 9) {printf("send failed\n");return -1;}usleep(365);memset(read_buf, 0, sizeof(read_buf));
count = read(fd, read_buf, sizeof(read_buf));if (count > 0) {for (i = 0; i < count; i++);
temp->temp_enviromem = read_buf[7] << 8 | read_buf[6];printf("temp->temp_enviromem = %f\n", temp->temp_enviromem * 0.01);} else {printf("read enviromem failed\n");return -1;}
count = write(fd, cmd_buf2, 9);if (count != 9) {printf("send failed\n");return -1;}usleep(70000);memset(read_buf, 0, sizeof(read_buf));memset(temp->temp_col, 0, sizeof(temp->temp_col));
count = read(fd, read_buf, sizeof(read_buf));printf("count = %d\n", count);if (count > 0) {for (i = 0; i < count - 7; i++)
temp->temp_col[i] = read_buf[i+6];for (i = 0; i < 1536; i++){if (!(i%10))printf("\n");printf("%#X ", temp->temp_col[i]);}} else {printf("read temp colour failed\n");return -1;}free(temp); 
close(fd);tcsetattr(fd, TCSANOW, &oldtio); // Restore the original settings return 0;}