分类讨论:单片机keil混合编程
我挑了proteus里的点阵显示器、字符lcd显示器和图形lcd显示器。点阵实验没做,认为就是多个led灯,也许我低估它了。其次简单的是字符显示器,我先花了点时间看proteus里的帮助文档,主要是液晶控制器44780的datasheet,然后就想自己写lcd的驱动程序。这才发觉我从来没写过驱动,我挑的字符lcd还只有一个串行输入,我根本不知道要传什么东西到那个输入,也没有相关的文档。无奈之下上网找别人写的单片机lcd驱动程序,挑了一个然后按照上面的步骤摆了器件,写了代码。很幸运的,程序成功的运行起来了。
可是让我不太满意的是,所有的代码都在一个文件里,而驱动应该是要独立出来的,还有像delay的函数几乎每个实验里都要用,完全可以单独出来做成一个库。基于这些代码结构上的要求,也为了以后写复杂的实验代码便于维护,多文件编译,甚至是混合编译就很必要了。
一共有三种不同格式的语言可能需要混合编译:C语言,内嵌汇编语言,汇编语言 。内嵌汇编用的也是汇编的语法,但和纯汇编还是有点区别的。总共有九种情况需要推敲:
1. C函数调用C函数
这个是基础,会C语言的都知道的,就不举例了。
2. C函数 调用 汇编函数或变量
2.1 调用汇编函数
2.1.1 没有参数
见 CInvokeAsmFunc1方法
2.1.2 带参数
2.1.2.1 使用寄存器存储参数
请参考Cx51编译器手册第六章“高级编程技巧”中第四节“C到汇编的接口” 。
2.1.2.1.1 寄存器参数传递规则
参数类型 | char | int | long/float | 通用指针 |
第1个 | R7 | R6&R7 | R4-R7 | R1-R3 |
第2个 | R5 | R4&R5 | R4-R7 | R1-R3 |
第3个 | R3 | R2&R3 | -- | R1-R3 |
返回值类型 | 寄存器 | 说明 |
Bit | C | 由具体标志位返回 |
char/unsigned char / 1 byte 指针 | R7 | |
int/unsigned int / 2 byte 指针 | R6&R7 | 高位在R6 |
long/unsigned long / 3 byte 指针 | R4-R7 | 高位在R4 |
float | R4-R7 | 32bit IEEE格式,指数和符号位在R7 |
通用指针 | R1-R3 | 存储类型在R3,高位在R2 |
见 CInvokeAsmFunc2方法
2.1.2.2 使用固定存储区存储参数
见CInvokeAsmFunc3方法
2.2 引用汇编变量
很不幸的告诉大家,我试了两天也无法让C程序识别到一个extern int型的汇编“变量”,百思不得其解。我试过引用一个char类型的变量,是可以识别的。我猜想的原因是:汇编中没有类型的概念,所以想让汇编把一个2bytes的xdata数据当成一个int型是不可能的。keil调试的时候就发现定义的外部int变量cReferenceAsmPara只有8位,汇编中我可是预留了2bytes的空间的呀。如果哪位高人有解决办法的话,麻烦告诉我一声,先谢谢了。
好一句"山穷水尽疑无路,柳暗花明又一村"!11月2号也就是今天上班的时候突然想到汇编中也有DW定义一个int型数据的呀?!怎么可能没有识别数据类型的能力呢?下班回家就又开始尝试,DW不能在data段里使用,把xdata改成data,加?dt?伪指令也不行,最后把int型高低字节互换了一下,居然可以了!反正就是Big-endian,Little-endian大小端的问题,在往变量里填字节时考虑过这个问题,但是没有尝试,才搞了这么久,真是不细心啊~~~
还好自己发现了,不然本文就存在一个不小的缺憾了。
见cReferenceAsmVar变量
3.C函数 调用 内嵌汇编函数或变量
没必要。直觉。
4. 汇编函数 调用 汇编函数或变量
4.1 调用汇编函数
见AsmInvokeAsmFunc 方法
4.2 引用汇编变量
4.2.1 引用代码段中定义的变量
要使用AX51,需在工程的选项对话框(Options for Target ...)的设备页(Device)勾选设备描述框右上角的两个复选框。
所以DD只能用Ax51编译。
用Ax51汇编器的话,有一个编译问题需要注意一下。就是p0-p3等一些在A51和Cx51里定义的符号在Ax51中没有,需要自己定义。在keil帮助里搜索p1然后看以Ax51为前缀的那些项。keil的帮助里Ax51的B. Reserved Symbols段里有这么几句话:
然后在SFR Assembler Statement段里有定义的示范代码:
sfr P1 = 0x90;
但是在代码段中用DD定义的变量无法在其他文件中引用。如果声明引用的是code,则编译报错没有数据地址;如果声明为data,则编译报错引用到错误的外部符号-_-。结论就是只能在一个文件中引用到code段中用DB,DW,DD定义的变量。(结论有问题,引用的方式不对)
测试发现代码段中用DB,DW,DD定义的是常量,无法当变量用,所以即使引用方式对了也没用。
见AsmReferenceAsmVar1“变量”
4.2.2 引用数据段中定义的变量
5. 汇编函数 调用 C函数或变量
5.1 调用C函数
5.1.1 寄存器存放参数
见AsmInvokeCFunc1方法
5.1.2 存储空间存放参数
见AsmInvokeCFunc2方法
5.2 引用C变量
见asmReferenceCVar变量
6. 汇编函数 调用 内嵌汇编函数或变量
也没有必要
7. 内嵌汇编函数 调用 内嵌汇编函数或变量
一般在同一个代码段里,没有连接的问题。不同内嵌代码之间的调用。。。还是算了吧。
8. 内嵌汇编函数 调用 C函数或变量
8.1 调用C函数
8.1.1 使用寄存器存储参数
8.1.2 使用存储空间存储参数
8.2 引用C变量
见embedAsmReferenceCVar变量
9.1 调用汇编函数
见EmbedAsmInvokeAsmFunc方法
9.2 引用汇编变量
见EmbedAsmReferenceAsmVar变量
测试源代码:
c_main.c:
* file name: c_main.c
* author: Prajna
* create date: 2011.10.30
* update history:
* [date] [coder] [update id] [comments]
* 2011/10/30 Prajna ciaf01 add CInvokeAsmFunc1 test case, no parameter
* 2011/10/30 Prajna ciaf02 add CInvokeAsmFunc2 test case, one parameter passing in register
* 2011/10/30 Prajna ciaf03 add CInvokeAsmFunc3 test case, two parameters passing in memory
* 2011/11/01 Prajna crav04 add cReferenceAsmVar test case
* 2011/11/03 Prajna aicf08 add _AsmInvokeCFunc1 test case, paramenter passing by register
* 2011/11/03 Prajna aicf09 add _AsmInvokeCFunc2 test case, paramenter passing by memory
* 2011/11/03 Prajna arcv10 add asmReferenceCVar test case
* 2011/11/04 Prajna eaicf11 add EmbedAsmInvokeCFunc1 test case, paramenter passing by register
* 2011/11/04 Prajna eaicf12 add EmbedAsmInvokeCFunc2 test case, paramenter passing by memory
* 2011/11/04 Prajna earc13 add embedAsmReferenceCVar test case
* 2011/11/05 Prajna eaia14 add EmbedAsmInvokeAsmFunc test case
* 2011/11/05 Prajna eara15 add EmbedAsmReferenceAsmVar test case
*******************************************************/
//#pragma NOREGPARMS//aicf09 //eaicf12
//#pragma SRC
#include <reg51.h>
#include <math.h>//aicf08
#include "ParaInMemory.h"//ciaf03
//sbit P2_3 = P2^3;//crav04
//sbit P2_6 = P2^6;//aicf08
//sbit P2_7 = P2^7;//aicf09
//sbit P1_1 = P1^1;//eaicf11
//sbit P1_2 = P1^2;//eaicf12
//int asmReferenceCVar;//arcv10
//int embedAsmReferenceCVar = 0x00FF;//earc13, leds behave weird when I set value to 0x5577
extern void AsmMain();
//extern void CInvokeAsmFunc1(void);//ciaf01
//extern void CInvokeAsmFunc2(char);//ciaf02
//extern int cReferenceAsmVar;//crav04
//aicf08 begin
//unsigned char AsmInvokeCFunc1(unsigned char x, unsigned int y)
//{
// int result = sqrt(y/x);
// if (4 == result) {
// P2_6 = ~P2_6;
// }
// return result;
//}
//aicf08 end
//aicf09 begin
//unsigned char AsmInvokeCFunc2(unsigned int v_a, unsigned int v_b)
//{
// int result = sqrt(v_a/v_b);
// if (4 == result) {
// P2_7 = ~P2_7;
// }
// return result;
//}
//aicf09 end
//eaicf11 begin
//unsigned char EmbedAsmInvokeCFunc1(unsigned char x, unsigned int y)
//{
//// int result = sqrt(y/x);
// int result = y/x;
// if (16 == result) {
// P1_1 = ~P1_1;
// }
// return result;
//}
//eaicf11 end
//eaicf12 begin
//unsigned char EmbedAsmInvokeCFunc2(unsigned char x, unsigned int y)
//{
//// int result = sqrt(y/x);
// int result = y/x;
// if (16 == result) {
// P1_2 = ~P1_2;
// }
// return result;
//}
//eaicf12 end
void main(void)
{
//asmReferenceCVar = 0xFF00;//arcv10
AsmMain();
//CInvokeAsmFunc1();//ciaf01
//CInvokeAsmFunc2(0x0AA);//ciaf02
//TestCInvokeAsmFunc3(0x0AA, 0x0102);//ciaf03
//crav04 begin
/* NOT 0x0605! Big-endian mode */
//if (0x0506 == cReferenceAsmVar) {
// P2_3 = ~P2_3;
//}
//crav04 end
#pragma asm
//extrn code (EmbedAsmInvokeAsmFunc) ;//eaia14
extrn data (EmbedAsmReferenceAsmVar) ;//eara15
//extrn code (_EmbedAsmInvokeCFunc1) ;//eaicf11, do not declair if in the same c file, or attributes conflict
//extrn code (EmbedAsmInvokeCFunc2) ;//eaicf12, do not declair if in the same c file, or attributes conflict
//extrn data (?EmbedAsmInvokeCFunc2?byte) ;//eaicf12, do not declair if in the same c file, or compile error
ljmp EMBED_START ;below segment code is not auto called
//eaicf12 begin
//EmbedAsmInvokeCFuncCode segment code ;
//VAR segment data overlayable ;local variable
//STACK segment idata ;
//rseg VAR ;
//v_x: DS 1 ;
//v_y: DS 2 ;
//result: DS 1 ;
//rseg STACK ;
//DS 20H ;
//rseg EmbedAsmInvokeCFuncCode ;
//eaicf12 end
EMBED_START:
//earc13 begin
// mov a, embedAsmReferenceCVar+1 ;Big-endia mode
// cjne a, #0FFH, C_COMPARE_FAILED ;
// cpl p1.3 ;
//earc13 end
//
//eaicf11 begin
// mov r7, #10H ;first char parameter x
// mov r4, #01H ;MSB byte of second int parameter y
// mov r5, #00H ;LSB byte of second int parameter y
// lcall _EmbedAsmInvokeCFunc1 ;
// mov a, r7 ;return value in r7
//eaicf11 end
//eaicf12 begin
// mov v_x+0, #10H ;
// mov v_y+0, #01H ;MSB
// mov v_y+1, #00H ;LSB
//
// mov sp, #STACK - 1 ;
// mov ?EmbedAsmInvokeCFunc2?byte+0, v_x+0 ;
// mov ?EmbedAsmInvokeCFunc2?byte+1, v_y+0 ;
// mov ?EmbedAsmInvokeCFunc2?byte+2, v_y+1 ;
// lcall EmbedAsmInvokeCFunc2 ;
// mov result, r7 ;
//eaicf12 end
//
// lcall EmbedAsmInvokeAsmFunc ;//eaia14
//
//eara15 begin
mov EmbedAsmReferenceAsmVar+0, #88H ;
mov EmbedAsmReferenceAsmVar+1, #99H ;
mov a, EmbedAsmReferenceAsmVar+0 ;
cjne a, #88H, C_COMPARE_FAILED ;
cpl p1.5 ;
//eara15 end
C_COMPARE_FAILED:
#pragma endasm
}
AsmMain.s:
* file name: AsmMain.s
* author: Prajna
* create date: 2011.10.30
* update history:
* [date] [coder] [update id] [comments]
* 2011/10/30 Prajna ciaf01 add CInvokeAsmFunc1 test case, no parameter
* 2011/10/30 Prajna ciaf02 add CInvokeAsmFunc2 test case, one parameter passing in register
* 2011/10/30 Prajna ciaf03 add CInvokeAsmFunc3 test case, two parameters passing in memory
* 2011/11/01 Prajna crav04 add cReferenceAsmVar test case
* 2011/11/01 Prajna aiaf05 add AsmInvokeAsmFunc test case
* 2011/11/02 Prajna crav06 add AsmReferenceAsmVar1 test case, 'variable' in code segment
* 2011/11/02 Prajna crav07 add AsmReferenceAsmVar2 test case, variable in data segment
* 2011/11/03 Prajna aicf08 add _AsmInvokeCFunc1 test case, paramenter passing by register
* 2011/11/03 Prajna aicf09 add _AsmInvokeCFunc2 test case, paramenter passing by memory
* 2011/11/03 Prajna arcv10 add asmReferenceCVar test case
*******************************************************/
$include (SfrDefinition.inc)
name ASM_MAIN_MODULE ;module name
/*-------------------------------------------------------*/
?pr?AsmMain?ASM_MAIN_MODULE segment code ;define segment in program region
public AsmMain ;helper function for c interface
//extrn code (AsmInvokeAsmFunc) ;//aiaf05
//extrn data (cReferenceAsmVar) ;//crav04
//extrn data (AsmReferenceAsmVar1) ;//crav06, variable in code segment
//extrn data (AsmReferenceAsmVar2) ;//crav07, variable in data segment
//extrn code (_AsmInvokeCFunc1) ;//aicf08
//extrn data (asmReferenceCVar) ;//arcv10
rseg ?pr?AsmMain?ASM_MAIN_MODULE ;procedure can be placed in any location by linker
AsmMain:
//crav04 begin ;initialize variable data
// mov cReferenceAsmVar+0, #05H ;MSB
// mov cReferenceAsmVar+1, #06H ;LSB
//crav04 end
// lcall AsmInvokeAsmFunc ;//aiaf05
// mov a, AsmReferenceAsmVar1 ;//crav06
//crav07 begin
// mov AsmReferenceAsmVar2+0, #11H ;
// mov AsmReferenceAsmVar2+1, #22H ;
// mov AsmReferenceAsmVar2+2, #33H ;
// mov AsmReferenceAsmVar2+3, #44H ;
// mov a, AsmReferenceAsmVar2+0 ;
// cjne a, #11H, COMPARE_FAILED ;
// cpl p2.5 ;
//crav07 end
//aicf08 begin
// mov r7, #10H ;first char parameter x
// mov r4, #01H ;MSB byte of second int parameter y
// mov r5, #00H ;LSB byte of second int parameter y
// lcall _AsmInvokeCFunc1 ;
// mov a, r7 ;
//aicf08 end
//aicf09 begin
// mov v_a+0, #01H ;initialize first parameter data
// mov v_a+1, #00H ;
// mov v_b+0, #00H ;initialize second parameter data
// mov v_b+1, #10H ;
// lcall AIC_START ;
//aicf09 end
//arcv10 begin
// mov a, asmReferenceCVar+0 ;Big-endia mode
// cjne a, #0FFH, COMPARE_FAILED ;
// cpl p1.0 ;
//arcv10 end
COMPARE_FAILED:
ret ;
//ciaf01 begin
/*-------------------------------------------------------*/
/*
* begin of segment name is PR,
* it is used for be compatible with C51 internal naming conversion,naming rule conversion below:
* CODE -?PR?
* XDATA-?XD
* DATA-?DT
* BIT-?BI
* PDATA-?PD
*/
//?pr?CInvokeAsmFunc1?ASM_MAIN_MODULE segment code ;define segment in program region
//public CInvokeAsmFunc1 ;module interface for other modules
//rseg ?pr?CInvokeAsmFunc1?ASM_MAIN_MODULE ;procedure can be placed in any location by linker
//CInvokeAsmFunc1:
// cpl p2.0 ;
// ret ;
//ciaf01 end
//ciaf02 begin
/*-------------------------------------------------------*/
//?pr?_CInvokeAsmFunc2?ASM_MAIN_MODULE segment code ;
//public _CInvokeAsmFunc2 ;with a parameter
//rseg ?pr?_CInvokeAsmFunc2?ASM_MAIN_MODULE ;
//_CInvokeAsmFunc2:
// mov a, r7 ;get first parameter of type char
// cjne a, #0AAH, PASS_PARAMETER_ERROR ;
// cpl p2.1 ;
// sjmp RETURN ;
//PASS_PARAMETER_ERROR:
// setb p2.1 ;
//RETURN:
// ret ;
//ciaf02 end
//ciaf03 begin
/*-------------------------------------------------------*/
//?pr?CInvokeAsmFunc3?ASM_MAIN_MODULE segment code ;
//?dt?CInvokeAsmFunc3?ASM_MAIN_MODULE segment data overlayable ;independent variable data segment declair
//public CInvokeAsmFunc3, ?CInvokeAsmFunc3?byte, ?CInvokeAsmFunc3?int ;with two parameter
//
//rseg ?dt?CInvokeAsmFunc3?ASM_MAIN_MODULE ;
//?CInvokeAsmFunc3?byte:
//para1: ds 1 ;
//?CInvokeAsmFunc3?int:
//para2: ds 2 ;
//
//rseg ?pr?CInvokeAsmFunc3?ASM_MAIN_MODULE ;
//CInvokeAsmFunc3:
// mov a, ?CInvokeAsmFunc3?byte+0 ;get first parameter of type char
// mov r4, ?CInvokeAsmFunc3?int+0 ;low byte of second parameter of type int
// mov r5, ?CinvokeAsmFunc3?int+1 ;high byte of second parameter of type int
// cjne a, #0AAH, PASS_PARAMETER_ERROR ;
// cpl p2.2 ;
// sjmp FINALLY ;
//PASS_PARAMETER_ERROR:
// setb p2.2 ;
//FINALLY:
// ret ;
//ciaf03 end
//aicf09 begin
/*-------------------------------------------------------*/
//extrn code (AsmInvokeCFunc2) ;
//extrn data (?AsmInvokeCFunc2?byte) ;byte type parameter definition
//AsmInvokeCFuncCode segment code ;
//VAR segment data ;local variable
//STACK segment idata ;
//rseg VAR ;
//v_a: DS 2 ;
//v_b: DS 2 ;
//result: DS 1 ;
//rseg STACK ;
//DS 20H ;
//rseg AsmInvokeCFuncCode ;
//AIC_START:
// mov sp, #STACK - 1 ;
// mov ?AsmInvokeCFunc2?byte+0, v_a+0 ;
// mov ?AsmInvokeCFunc2?byte+1, v_a+1 ;
// mov ?AsmInvokeCFunc2?byte+2, v_b+0 ;
// mov ?AsmInvokeCFunc2?byte+3, v_b+1 ;
// lcall AsmInvokeCFunc2 ;
// mov result, r7 ;
// ret ;
//aicf09 end
/*-------------------------------------------------------*/
end
partner.s:
* file name: partner.s
* author: Prajna
* create date: 2011.10.30
* update history:
* 2011/11/01 Prajna crav04 add cReferenceAsmVar test case
* 2011/11/01 Prajna aiaf05 add AsmInvokeAsmFunc test case
* 2011/11/02 Prajna crav06 add AsmReferenceAsmVar1 test case, 'variable' in code segment
* 2011/11/02 Prajna crav07 add AsmReferenceAsmVar2 test case, variable in data segment
* 2011/11/05 Prajna eaia14 add EmbedAsmInvokeAsmFunc test case
* 2011/11/05 Prajna eara15 add EmbedAsmReferenceAsmVar test case
*******************************************************/
//$include (SfrDefinition.inc)
PARTNER segment code ;
//public AsmInvokeAsmFunc ;//aiaf05
//public EmbedAsmInvokeAsmFunc ;//eaia14
//public AsmReferenceAsmVar1 ;//crav06
rseg PARTNER
//aiaf05 begin
//AsmInvokeAsmFunc:
// cpl p2.4 ;
// mov dptr, #AsmReferenceAsmVar1 ;
// movc a, @a + dptr ;
// ret ;
//aiaf05 end
//crav06 begin
//AsmReferenceAsmVar1:
//DD 11223344H ;only Ax51 can assemble 32bits variable
//crav06 end
//eaia14 begin
//EmbedAsmInvokeAsmFunc:
// cpl p1.4 ;
// ret ;
//eaia14 end
nop ;
ReferenceAsmData segment data ;
//public cReferenceAsmVar ;//crav04
//public AsmReferenceAsmVar2 ;//crav07
public EmbedAsmReferenceAsmVar ;//eara15
rseg ReferenceAsmData ;
//cReferenceAsmVar: DS 2 ;//crav04
//AsmReferenceAsmVar2: DS 4 ;//crav07
//EmbedAsmReferenceAsmVar:
//DB 07H ;DB, DW, DD can only define constants
EmbedAsmReferenceAsmVar: DS 2 ;//eara15
temp: DS 1 ; ;
end
ParaInMemory.c:
* file name: ParaInMemory.c
* author: Prajna
* create date: 2011.10.31
* update history:
* [date] [coder] [update id] [comments]
* 2011/10/30 Prajna ciaf03 add CInvokeAsmFunc3 test case, two parameters passing in memory
*******************************************************/
#include <reg51.h>
#include "ParaInMemory.h"
//char TestCInvokeAsmFunc3(char para1, int para2)
//{
// return CInvokeAsmFunc3(para1, para2);//ciaf03
//}
ParaInMemory.h:
* file name: ParaInMemory.h
* author: Prajna
* create date: 2011.10.31
* update history:
* 2011/10/30 Prajna ciaf03 add CInvokeAsmFunc3 test case, two parameters passing in memory
*******************************************************/
#ifndef H_PARAMETER_IN_MEMORY_H
#define H_PARAMETER_IN_MEMORY_H
//ciaf03 begin
/*
* NOTE:
* NOREGPARMS compilation configuration has global influence. so if used,
* other functions that pass parameters by registers also need to define data segment
* to save parameters
*/
//#pragma NOREGPARMS //forbidden using reg to pass parameters, only using memory
//extern char CInvokeAsmFunc3(char para1, int para2);
////ciaf03 end
//char TestCInvokeAsmFunc3(char para1, int para2);
#endif
SfrDefinition.inc:
* file name: SfrDefinition.inc
* author: Prajna
* create date: 2011.11.03
* update history:
* [date] [coder] [update id] [comments]
* 2011/11/02 Prajna crav07 add AsmReferenceAsmVar2 test case, variable in data segment
*******************************************************/
//crav07 begin
//sfr p0 = 0x80 ;sfr should be defined when using Ax51 assembler.
//sfr p1 = 0x90 ;
//sfr p2 = 0x0A0 ;
//sfr p3 = 0x0B0 ;
//crav07 end
参考:
【1】
作者不详
【2】
【3】
作者 徐卫华
【4】
【5】
【6】
【7】
【8】
作者不详
【10】