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 }
(指南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_SYSCLK2,PLL1_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)