At the simplest level, you could say a simple communication protocol has three layers: physical, transport, and application. (There are models with more such as OSI with 7 or TCP/IP with 4. The number of layers isn't terribly important in the context of this question.)
The application layer is the layer you deal with directly in your code, and the focus of the question. As far as the transport layer is concerned, the byte you passed to it in send_data is just a binary pattern, but you may interpret it in your application code as the letter 'A'. The CRC or checksum calculation will be the same regardless of whether you consider the byte to be 'A,' 0x41, or 0b01000001.
The transport layer is the packet level, where you have your message headers, and error checking, whether it be CRC or a basic checksum. In the context of firmware, you may have a function such as send_data, where you pass it a byte to send. Inside that function it put into a packet that says, "Hey this is a normal message, requires an acknowledgement, and the checksum is 0x47, current time is X." This packet is sent out over the physical layer to the receiving node.
The physical layer is where the electronics and interface are defined: connectors, voltage levels, timing, etc. This layer could range from a couple of traces running TTL signals for a basic UART on a PCB, to a fully isolated differential pair as in some CAN implementations.
At the receiving node, the packet comes in on the physical layer, is unpacked at the transport layer, and then your binary pattern is available to the application layer. It's up to the receiving node application layer to know whether that pattern should be interpreted as 'A,' 0x41, or 0b01000001, and what to do with it.
In conclusion, it's pretty much always acceptable to send ASCII characters if that is what the application requires. The important thing is to understand your communication scheme, and include an error checking mechanism.