在 PIC18 上写 UART
Write UART on PIC18
我需要有关 uart 通信方面的帮助,我正在尝试在我的 Proteus 仿真中实现。我使用 PIC18f4520,我想在虚拟终端上显示微控制器计算出的值。
Here a snap of my design on Proteus
现在,这就是我的 UART 代码的样子:
#define _XTAL_FREQ 20000000
#define _BAUDRATE 9600
void Configuration_ISR(void) {
IPR1bits.TMR1IP = 1; // TMR1 Overflow Interrupt Priority - High
PIE1bits.TMR1IE = 1; // TMR1 Overflow Interrupt Enable
PIR1bits.TMR1IF = 0; // TMR1 Overflow Interrupt Flag
// 0 = TMR1 register did not overflow
// 1 = TMR1 register overflowed (must be cleared in software)
RCONbits.IPEN = 1; // Interrupt Priority High level
INTCONbits.PEIE = 1; // Enables all low-priority peripheral interrupts
//INTCONbits.GIE = 1; // Enables all high-priority interrupts
}
void Configuration_UART(void) {
TRISCbits.TRISC6 = 0;
TRISCbits.TRISC7 = 1;
SPBRG = ((_XTAL_FREQ/16)/_BAUDRATE)-1;
//RCSTA REG
RCSTAbits.SPEN = 1; // enable serial port pins
RCSTAbits.RX9 = 0;
//TXSTA REG
TXSTAbits.BRGH = 1; // fast baudrate
TXSTAbits.SYNC = 0; // asynchronous
TXSTAbits.TX9 = 0; // 8-bit transmission
TXSTAbits.TXEN = 1; // enble transmitter
}
void WriteByte_UART(unsigned char ch) {
while(!PIR1bits.TXIF); // Wait for TXIF flag Set which indicates
// TXREG register is empty
TXREG = ch; // Transmitt data to UART
}
void WriteString_UART(char *data) {
while(*data){
WriteByte_UART(*data++);
}
}
unsigned char ReceiveByte_UART(void) {
if(RCSTAbits.OERR) {
RCSTAbits.CREN = 0;
RCSTAbits.CREN = 1;
}
while(!PIR1bits.RCIF); //Wait for a byte
return RCREG;
}
在主循环中:
while(1) {
WriteByte_UART('a'); // This works. I can see the As in the terminal
WriteString_UART("Hello World !"); //Nothing displayed :(
}//end while(1)
我已经为 WriteString_UART 尝试了不同的解决方案,但 none 目前有效。
我不想使用 printf 因为它会通过增加延迟影响我正在对 PIC 执行的其他操作。
所以我真的想让它与 WriteString_UART 一起使用。
最后我想在终端上有类似 "Error rate is : [a value]%" 的东西。
感谢您的帮助,如有不明之处请告诉我。
在行后添加几毫秒的延迟
TXREG = ch;
验证 WriteString_UART(char *data) 的指针 *data 实际上指向
字符串 "Hello World !".
在您的 WriteByte_UART()
函数中,尝试轮询 TRMT 位。特别是,更改:
while(!PIR1bits.TXIF);
至
while(!TXSTA1bits.TRMT);
我不知道这是否是您的特定问题,但由于在加载 TXREG 时 TXIF 没有立即被清除,因此存在竞争条件。另一种选择是尝试:
...
Nop();
while(!PIR1bits.TXIF);
...
根据评论进行编辑
问题是由于PIC18使用基于数据存储器和程序存储器的两种不同指针类型。尝试将您的声明更改为 void WriteString_UART(const rom char * data)
,看看会发生什么。您还需要将 WriteByte_UART()
声明更改为 void WriteByte_UART(const unsigned char ch)
.
您似乎找到了解决方案,但最初无法解决的原因仍不清楚。您使用的是什么编译器?
我了解到 C18 和 XC8 在内存空间方面的使用方式不同。对于这两种编译器,字面上声明的字符串如 char string[]="Hello!"
将存储在 ROM(程序存储器)中。它们在函数使用字符串的方式上有所不同。
C18 字符串函数将有变体来访问 RAM 或 ROM 中的字符串(例如 strcpypgm2ram
、strcpyram2pgm
等)。另一方面,XC8 会为您完成这项工作,您无需使用特定功能来选择要访问的内存。
如果您使用的是 C18,我强烈建议您切换到 XC8,它更新且更易于使用。如果您仍然想使用 C18 或其他需要处理 program/data 内存空间的编译器,那么下面是您可能想尝试的两种解决方案。 C18 数据表说 putsUSART
将字符串从数据存储器打印到 USART。函数 putrsUSART
将从程序内存中打印一个字符串。所以你可以简单地使用 putrsUSART
来打印你的字符串。
您可能还想尝试以下方法,包括将字符串从程序内存复制到数据内存(如果您的应用程序内存紧张,这可能会浪费内存):
char pgmstring[] = "Hello";
char datstring[16];
strcpypgm2ram(datstring, pgmstring);
putsUSART(datstring);
在这个例子中,指针pgmstring
和datstring
将被存储在数据存储器中。字符串 "Hello"
将存储在程序存储器中。所以即使指针pgmstring
本身在数据内存中,它最初也指向一个内存地址("Hello"
的地址)。在数据存储器中指向同一个字符串的唯一方法是在数据存储器中创建它的一个副本。这是因为接受存储在数据存储器中的字符串的函数(例如 putsUSART
)不能直接与存储在程序存储器中的字符串一起使用。
我希望这可以帮助您更好地理解如何使用程序和数据存储器分离的哈佛微处理器。
我需要有关 uart 通信方面的帮助,我正在尝试在我的 Proteus 仿真中实现。我使用 PIC18f4520,我想在虚拟终端上显示微控制器计算出的值。
Here a snap of my design on Proteus
现在,这就是我的 UART 代码的样子:
#define _XTAL_FREQ 20000000
#define _BAUDRATE 9600
void Configuration_ISR(void) {
IPR1bits.TMR1IP = 1; // TMR1 Overflow Interrupt Priority - High
PIE1bits.TMR1IE = 1; // TMR1 Overflow Interrupt Enable
PIR1bits.TMR1IF = 0; // TMR1 Overflow Interrupt Flag
// 0 = TMR1 register did not overflow
// 1 = TMR1 register overflowed (must be cleared in software)
RCONbits.IPEN = 1; // Interrupt Priority High level
INTCONbits.PEIE = 1; // Enables all low-priority peripheral interrupts
//INTCONbits.GIE = 1; // Enables all high-priority interrupts
}
void Configuration_UART(void) {
TRISCbits.TRISC6 = 0;
TRISCbits.TRISC7 = 1;
SPBRG = ((_XTAL_FREQ/16)/_BAUDRATE)-1;
//RCSTA REG
RCSTAbits.SPEN = 1; // enable serial port pins
RCSTAbits.RX9 = 0;
//TXSTA REG
TXSTAbits.BRGH = 1; // fast baudrate
TXSTAbits.SYNC = 0; // asynchronous
TXSTAbits.TX9 = 0; // 8-bit transmission
TXSTAbits.TXEN = 1; // enble transmitter
}
void WriteByte_UART(unsigned char ch) {
while(!PIR1bits.TXIF); // Wait for TXIF flag Set which indicates
// TXREG register is empty
TXREG = ch; // Transmitt data to UART
}
void WriteString_UART(char *data) {
while(*data){
WriteByte_UART(*data++);
}
}
unsigned char ReceiveByte_UART(void) {
if(RCSTAbits.OERR) {
RCSTAbits.CREN = 0;
RCSTAbits.CREN = 1;
}
while(!PIR1bits.RCIF); //Wait for a byte
return RCREG;
}
在主循环中:
while(1) {
WriteByte_UART('a'); // This works. I can see the As in the terminal
WriteString_UART("Hello World !"); //Nothing displayed :(
}//end while(1)
我已经为 WriteString_UART 尝试了不同的解决方案,但 none 目前有效。
我不想使用 printf 因为它会通过增加延迟影响我正在对 PIC 执行的其他操作。 所以我真的想让它与 WriteString_UART 一起使用。 最后我想在终端上有类似 "Error rate is : [a value]%" 的东西。
感谢您的帮助,如有不明之处请告诉我。
在行后添加几毫秒的延迟 TXREG = ch;
验证 WriteString_UART(char *data) 的指针 *data 实际上指向
字符串 "Hello World !".
在您的 WriteByte_UART()
函数中,尝试轮询 TRMT 位。特别是,更改:
while(!PIR1bits.TXIF);
至
while(!TXSTA1bits.TRMT);
我不知道这是否是您的特定问题,但由于在加载 TXREG 时 TXIF 没有立即被清除,因此存在竞争条件。另一种选择是尝试:
...
Nop();
while(!PIR1bits.TXIF);
...
根据评论进行编辑
问题是由于PIC18使用基于数据存储器和程序存储器的两种不同指针类型。尝试将您的声明更改为 void WriteString_UART(const rom char * data)
,看看会发生什么。您还需要将 WriteByte_UART()
声明更改为 void WriteByte_UART(const unsigned char ch)
.
您似乎找到了解决方案,但最初无法解决的原因仍不清楚。您使用的是什么编译器?
我了解到 C18 和 XC8 在内存空间方面的使用方式不同。对于这两种编译器,字面上声明的字符串如 char string[]="Hello!"
将存储在 ROM(程序存储器)中。它们在函数使用字符串的方式上有所不同。
C18 字符串函数将有变体来访问 RAM 或 ROM 中的字符串(例如 strcpypgm2ram
、strcpyram2pgm
等)。另一方面,XC8 会为您完成这项工作,您无需使用特定功能来选择要访问的内存。
如果您使用的是 C18,我强烈建议您切换到 XC8,它更新且更易于使用。如果您仍然想使用 C18 或其他需要处理 program/data 内存空间的编译器,那么下面是您可能想尝试的两种解决方案。 C18 数据表说 putsUSART
将字符串从数据存储器打印到 USART。函数 putrsUSART
将从程序内存中打印一个字符串。所以你可以简单地使用 putrsUSART
来打印你的字符串。
您可能还想尝试以下方法,包括将字符串从程序内存复制到数据内存(如果您的应用程序内存紧张,这可能会浪费内存):
char pgmstring[] = "Hello";
char datstring[16];
strcpypgm2ram(datstring, pgmstring);
putsUSART(datstring);
在这个例子中,指针pgmstring
和datstring
将被存储在数据存储器中。字符串 "Hello"
将存储在程序存储器中。所以即使指针pgmstring
本身在数据内存中,它最初也指向一个内存地址("Hello"
的地址)。在数据存储器中指向同一个字符串的唯一方法是在数据存储器中创建它的一个副本。这是因为接受存储在数据存储器中的字符串的函数(例如 putsUSART
)不能直接与存储在程序存储器中的字符串一起使用。
我希望这可以帮助您更好地理解如何使用程序和数据存储器分离的哈佛微处理器。