C6748_ RS485(2)_通信原理

时间:2024-03-17 13:45:33

  485和232都是基于串口的通讯接口,在数据的收发操作上都是一致的。但是他两的通讯模式却大不相同~!232是全双工(例:A->B的同时B->A,瞬时同步)工作模式,而485是半双工(发时不能收,收时不能发)工作模式。在232通信中,主机在发送数据的同时可以收到从机发过来的数据;但在485通信中,收发要经过模式位的切换来进行,譬如,发送数据时,会把模式为置‘1’,表示为发送模式,此时不能接收;当接收数据时,会把模式位置‘0’,表示为接收模式,此时不能发送。

  RS485接口具有抗干扰性强,适合长距离传输和多站点通讯等特点,因此在工业控制中被普遍使用

  C6748开发板上的RS485的使能端与UART接口复用,而UART使能端与GPIO接口复用

  C6748的UART1作为收发串口,输入的ttl电平信号通过ttl转485电平转换电路转为485信号,再送到485通信设备,ttl转485电平转换电路如图

  UART1_RTSn(RequestToSend)UART1_CTSn(ClearToSend),控制UART的使能端。

   因此,可以通过控制GPIO0[11]的输出电平进而控制RS485的使能端,控制485的发送和接收。

主函数如下:

 1 intmain(void)
 2 {
 3     // 外设使能配置
 4     PSCInit();
 5     
 6     // GPIO 管脚复用配置
 7     GPIOBankPinMuxSet();
 8  
 9     // UART 初始化
10     UARTInit();
11  
12     // 发送字符串
13     // 使能发送
14     GPIOPinWrite(SOC_GPIO_0_REGS, 12, GPIO_PIN_HIGH);
15     unsignedchar i;
16     for(i = 0; i < 34; i++)
17         UARTCharPut(SOC_UART_1_REGS, Send[i]);
18     // 禁用发送
19     GPIOPinWrite(SOC_GPIO_0_REGS, 12, GPIO_PIN_LOW);
20  
21     // 主循环
22     for(;;)
23     {
24         Receive=UARTCharGet(SOC_UART_1_REGS);
25  
26         // 使能发送
27         GPIOPinWrite(SOC_GPIO_0_REGS, 12, GPIO_PIN_HIGH);
28         UARTCharPut(SOC_UART_1_REGS, Receive);
29         // 等待数据发送完成
30         for(i=0;i<200;i++);
31         // 禁用发送
32         GPIOPinWrite(SOC_GPIO_0_REGS, 12, GPIO_PIN_LOW);
33     }
34 }

(1)PSC配置

  先是进行外设使能配置,PSC是C6748开发板的电源及睡眠控制器,负责管理系统的电源开/闭的转换、时钟的开/闭以及复位。在这里主要是设置电源供电,为DSP芯片节能,不使用的接口可以不上电。

  int PSCModuleControl (unsigned int baseAdd, unsigned int moduleId,unsigned int powerDomain, unsigned int flags):‘baseAdd’为外设使用的寄存器基地址,“moduleId”为设备类型(偏移地址),“powerDomain”为未对端口进行操作时端口的初始状态;“flags”为上电类型,“1”为使能,“0”为未使能。配置寄存器方法:基本地址+偏移地址=相应的值。在数据手册里面如下:基本地址:#define SOC_PSC_1_REGS   (0x01E27000)   定义在soc_C6748.h 文件中;偏移地址:#define PSC_PTCMD        (0x120)   定义在hw_psc_C6748.h 文件中

1 void PSCInit(void)
2 {
3 // 使能 GPIO 模块
4 // 对相应外设模块的使能也可以在 BootLoader 中完成
5 PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_GPIO, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);
6 // 使能 UART1 模块
7 PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_UART1, PSC_POWERDOMAIN_ALWAYS_ON,PSC_MDCTL_NEXT_ENABLE);
8 }

  RS485使能端与UART1复用,而GPIO,UART的电源配置在PLLC1中,故其内存地为SOC_PSC_1_REGS,HW_PSC_GPIO=3;HW_PSC_UART1=12;PSC_MDCTL_NEXT_ENABLE=0x00000003u(U表示该常数用无符号整型方式存储);PSC_MDCTL_NEXT_DISABLE =0x00000002u

(指南P165)

  PSCModuleControl()函数具体如下:

 1 int PSCModuleControl (unsigned int baseAdd, unsigned int moduleId,
 2                          unsigned int powerDomain, unsigned int flags)
 3 {
 4     volatile unsigned int timeout = 0xFFFFFF;
 5     int    retVal = 0;
 6     unsigned int    status = 0;
 7 
 8     HWREG(baseAdd +  PSC_MDCTL(moduleId)) = (flags & 0x8000001Fu);
 9 
10     if (powerDomain == 0)
11     {
12         HWREG(baseAdd + PSC_PTCMD) = PSC_PTCMD_GO0;
13     }
14     else
15     {
16         HWREG(baseAdd + PSC_PTCMD) = PSC_PTCMD_GO1;
17     }
18 
19     if (powerDomain == 0)
20     {
21         do {
22             status = HWREG(baseAdd + PSC_PTSTAT) & PSC_PTSTAT_GOSTAT0;
23         } while (status && timeout--);
24     }
25     else
26     {
27         do {
28             status = HWREG(baseAdd + PSC_PTSTAT) & PSC_PTSTAT_GOSTAT1;
29         } while (status && timeout--);
30     }
31 
32     if (timeout != 0)
33     {
34         timeout = 0xFFFFFF;
35         status = flags & PSC_MDCTL_NEXT;
36         do {
37             timeout--;
38         } while(timeout &&
39                 (HWREG(baseAdd + PSC_MDSTAT(moduleId)) & PSC_MDSTAT_STATE) != status);
40     }
41 
42     if (timeout == 0)
43     {
44         retVal = -1;
45     }
46 
47     return retVal;
48 }

  HWREG(x)函数为读取寄存器宏定义,可以对寄存器x里面的数据进行访问,并读写。#define PSC_MDCTL(n) (0xA00 + (n * 4)),flag配置时对于GPIO和UART所访问的寄存器地址都位于“模块控制 n 寄存器” .

(指南P173)

(指南P189) 

(2)GPIOBankPinMuxSet()配置

  对DSP的复用管脚进行配置,GPIOBankPinMuxSet();函数如下:

1 voidGPIOBankPinMuxSet(void)
2 {
3     // 使能 UART1 禁用流控
4     UARTPinMuxSetup(1, FALSE);
5     // RS485 Enable 管脚
6     RS485PinMuxSetup();
7 }

 void UARTPinMuxSetup(unsigned int instanceNum, unsigned int modemCtrlChoice)​功能:设置UART的引脚,在片上系统(SOC)中,多路复用管脚是和外围设备共用的​

  instanceNum: 被使用的UART实例编号

  modemCtrlChoice: 是否使用modem控制,一般不需要,设置成FALSE​​,TURE则选择的是UART_RTS或UART_CTS(即流控)。

UARTPinMuxSetup(1, FALSE);函数设置PINMUX4寄存器的PINMUX4_31_28字段为2h,PINMUX4_27_24字段为2h,使能UART1所在引脚的UART1_TX和UART1_RX功能。UARTPinMuxSetup函数如下:

 1 voidUARTPinMuxSetup(unsignedint instanceNum, unsignedint modemCtrlChoice)
 2 {
 3 unsignedint svPinMuxRtsCts = 0;
 4 unsignedint svPinMuxTxdRxd = 0;
 5  
 6 if(0 == instanceNum)
 7 {
 8 if(TRUE == modemCtrlChoice)
 9 {
10 svPinMuxRtsCts = (HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(3)) & \
11 ~(SYSCFG_PINMUX3_PINMUX3_27_24 | \
12 SYSCFG_PINMUX3_PINMUX3_31_28));
13  
14 HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(3)) = \
15 (PINMUX3_UART0_CTS_ENABLE | \
16 PINMUX3_UART0_RTS_ENABLE | \
17 svPinMuxRtsCts);
18 }
19  
20 svPinMuxTxdRxd = (HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(3)) & \
21 ~(SYSCFG_PINMUX3_PINMUX3_23_20 | \
22 SYSCFG_PINMUX3_PINMUX3_19_16));
23  
24 HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(3)) = \
25 (PINMUX3_UART0_TXD_ENABLE | \
26 PINMUX3_UART0_RXD_ENABLE | \
27 svPinMuxTxdRxd);
28 }
29  
30 elseif(1 == instanceNum)
31 {
32 if(TRUE == modemCtrlChoice)
33 {
34 svPinMuxRtsCts = (HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(0)) & \
35 ~(SYSCFG_PINMUX0_PINMUX0_23_20 | \
36 SYSCFG_PINMUX0_PINMUX0_19_16));
37  
38 HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(0)) = \
39 (PINMUX0_UART1_CTS_ENABLE | \
40 PINMUX0_UART1_RTS_ENABLE | \
41 svPinMuxRtsCts);
42 }
43  
44 svPinMuxTxdRxd = (HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) & \
45 ~(SYSCFG_PINMUX4_PINMUX4_31_28 | \
46 SYSCFG_PINMUX4_PINMUX4_27_24));
47  
48 HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) = \
49 (PINMUX4_UART1_TXD_ENABLE | \
50 PINMUX4_UART1_RXD_ENABLE | \
51 svPinMuxTxdRxd);
52 }
53  
54 elseif(2 == instanceNum)
55 {
56  
57 if(TRUE == modemCtrlChoice)
58 {
59 svPinMuxRtsCts = (HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(0)) & \
60 ~(SYSCFG_PINMUX0_PINMUX0_31_28 | \
61 SYSCFG_PINMUX0_PINMUX0_27_24));
62  
63 HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(0)) = \
64 (PINMUX0_UART2_CTS_ENABLE | \
65 PINMUX0_UART2_RTS_ENABLE | \
66 svPinMuxRtsCts);
67 }
68  
69 svPinMuxTxdRxd = (HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) & \
70 ~(SYSCFG_PINMUX4_PINMUX4_23_20 | \
71 SYSCFG_PINMUX4_PINMUX4_19_16));
72  
73 HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) = \
74 (PINMUX4_UART2_TXD_ENABLE | \
75 PINMUX4_UART2_RXD_ENABLE | \
76 svPinMuxTxdRxd);
77  
78 }
79  
80 else
81 {
82  
83 }
84 }
View Code

(指南P204)

(指南P225)

(指南P225)

  RS485PinMuxSetup();函数使能RS485管脚,函数如下:

 1 void RS485PinMuxSetup(void)
 2 {
 3 unsignedint savePinMux = 0;
 4  
 5 savePinMux = (HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(0)) & \
 6                      ~(SYSCFG_PINMUX0_PINMUX0_19_16 | \
 7                      SYSCFG_PINMUX0_PINMUX0_19_16));
 8  
 9     HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(0)) = (PINMUX0_RS485_ENABLE | savePinMux);
10 }

  该函数设置PINMUX0寄存器的PINMUX0_19_16字段为8,将GP0[11]功能所在复用引脚的功能设为GP0[11],GP0[11]外接到ttl转485芯片(ISO3082DW)的使能引脚REn和DE,控制485芯片功能的使能和禁止。该485芯片电路的工作方式为半双工,发送时,要使能DE脚(Data Enable),GP0[11]为高;接收时,要使能REn脚(Receive enable),GP0[11]为低

(指南P204)

(指南P218)

(3)UARTInit()配置

  GPIO管脚复用配置好后,对UART初始化,初始化函数UARTInit();函数如下:

 1 voidUARTInit(void)
 2 {
 3     // 配置 UART1 参数
 4     // 波特率 115200 数据位 8 停止位 1 无校验位
 5 UARTConfigSetExpClk(SOC_UART_1_REGS, UART_1_FREQ, BAUD_115200, UART_WORDL_8BITS, UART_OVER_SAMP_RATE_16);
 6     // 使能 UART1
 7     UARTEnable(SOC_UART_1_REGS);
 8  
 9     // 设置使能管脚为输出状态 GPIO0[11]
10     GPIODirModeSet(SOC_GPIO_0_REGS, 12, GPIO_DIR_OUTPUT);
11 }

  UART初始化函数中,首先配置UART1的参数。UART1输入时钟为PLL1_SYSCLK2PLL1_SYSCLK2是PLL1_SYSCLK1的2分频,PLL1_SYSCLK1的频率默认为456MHz.。UARTConfigSetExpClk()函数中的UART_OVER_SAMP_RATE_16表示以16倍过采样率,即16个波特时钟完成1bit的传输。UART_1_FREQ表示UART所采用的时钟频率。

(指南P131)

(指南P133)

  定义UART2输入时钟频率如下:

1 // 时钟
2 #define SYSCLK_1_FREQ     (456000000)
3 #define SYSCLK_2_FREQ     (SYSCLK_1_FREQ/2)
4 #define UART_1_FREQ       (SYSCLK_2_FREQ)

  其次使能UART1,函数没有使能UART1的接收和发送FIFO,所以每次收发都是一个字节。然后,设置485芯片使能引脚GP0[11]为输出状态。GPIODirModeSet函数如下:

 1 voidGPIODirModeSet(unsignedint baseAdd, unsignedint pinNumber,
 2 unsignedint pinDir)
 3  
 4 {
 5 unsignedint regNumber = 0;
 6 unsignedint pinOffset = 0;
 7  
 8 /*
 9 ** Each register contains settings for each pin of two banks. The 32 bits
10 ** represent 16 pins each from the banks. Thus the register number must be
11 ** calculated based on 32 pins boundary.
12 */
13 regNumber = (pinNumber - 1)/32;
14  
15 /*
16 ** In every register the least significant bits starts with a GPIO number on
17 ** a boundary of 32. Thus the pin offset must be calculated based on 32
18 ** pins boundary. Ex: \'pinNumber\' of 1 corresponds to bit 0 in
19 ** \'register_name01\'.
20 */
21 pinOffset = (pinNumber - 1) % 32;
22  
23 if(GPIO_DIR_OUTPUT == pinDir)
24 {
25 HWREG(baseAdd + GPIO_DIR(regNumber)) &= ~(1 << pinOffset);
26 }
27 else
28 {
29 HWREG(baseAdd + GPIO_DIR(regNumber)) |= (1 << pinOffset);
30 }
31 }

(手册P254)

(指南P846)

 

(指南P849)

(4)GPIOPinWrite()配置

  完成UART1的初始化之后,就完成了所有的初始化工作了。这时,要发送数据到上位机的话,需要先使能485芯片,所以主函数先设置GP0[11]的输出电平为高,

  GPIOPinWrite(SOC_GPIO_0_REGS, 12, GPIO_PIN_HIGH);。函数如下:

 1 voidGPIOPinWrite(unsignedint baseAdd, unsignedint pinNumber,
 2 unsignedint bitValue)
 3 {
 4 unsignedintregNumber = 0;
 5 unsignedint pinOffset = 0;
 6  
 7 /*
 8 ** Each register contains settings for each pin of two banks. The 32 bits
 9 ** represent 16 pins each from the banks. Thus the register number must be
10 ** calculated based on 32 pins boundary.
11 */
12  
13 regNumber = (pinNumber - 1)/32;
14  
15 /*
16 ** In every register the least significant bits starts with a GPIO number on
17 ** a boundary of 32. Thus the pin offset must be calculated based on 32
18 ** pins boundary. Ex: \'pinNumber\' of 1 corresponds to bit 0 in
19 ** \'register_name01\'.
20 */
21  
22 pinOffset = (pinNumber - 1) % 32;
23  
24 if(GPIO_PIN_LOW == bitValue)
25 {
26 HWREG(baseAdd + GPIO_CLR_DATA(regNumber)) = (1 << pinOffset);
27 }
28 elseif(GPIO_PIN_HIGH == bitValue)
29 {
30 HWREG(baseAdd + GPIO_SET_DATA(regNumber)) = (1 << pinOffset);
31 }
32 }

  设置GP0[11]脚的输出为高,需要设置SET_DATA01寄存器的11位为1

 

(指南P846)

(指南P853)

  使能了485芯片的数据发送功能后,就可以往UART1写数据了。UARTCharPut(SOC_UART_1_REGS, Send[i]);将定义好的数据写往UART1的THR(transmitter holding register,THR)。然后禁用485芯片的发送功能,将GP0[11]清0,GPIOPinWrite(SOC_GPIO_0_REGS, 12, GPIO_PIN_LOW);。这时,485芯片处于接收状态,UART1等待上位机发送数据过来,Receive=UARTCharGet(SOC_UART_1_REGS);。收到数据后,再次启用发送功能,将收到的数据原封不动地发回去。

UART串口通信时的寄存器变化

  UART接收部分包括接收保持寄存器 (RSR) 和一个发送移位寄存器 (RBR)。在非 FIFO 模式,当一个字符到达 RBR 且中断使能寄存器 (IER) 得接收数据准备好中断使能,便产生一个中 断。当 RBR 内的字符被读取中断清除。在 FIFO 模式,当 FIFO 填充到 FIFO 控制寄存器 (FCR) 内选择的 触发水平便产生一个中断,当 FIFO 内容低于触发水平中断将清除。

  UART接收数据时,当串口将数据发送到PC端,此时UART中的的LSR寄存器的DR变为1,表示接受到了完整的字符,并传输到了接收缓冲器(RBR),当UARTCharGet()函数执行完,自动清零。而在RBR和THR的DATA寄存器中存储着接收到的数据(注意:RBR、THR和DLL共用一个地址,当 LCR 的为 DLAB 位 0,读取该地址得到 RBR 的内容,写入该 地址改变 THR。当 DLAB = 1,所有访问该地址的读或写都是 DLL。DLL 也可以使用地址偏移量 20h 访问

(指南P1496)

(指南P1484)

  当UART发送数据时,会不停的查询LSR的TEMT和THRE两位,为1表示THR和TSR为空,则可以向THR中写一字节的数据(由于发送数据较快,一般可以保持为1)。

(指南P1495)